diff options
author | Sam Leffler <sam@FreeBSD.org> | 2009-03-01 22:10:07 +0000 |
---|---|---|
committer | Sam Leffler <sam@FreeBSD.org> | 2009-03-01 22:10:07 +0000 |
commit | b2cbddbd4361766d55c5f9dae81f71f44577bb6a (patch) | |
tree | 372b16f9e6ccba7284b53135005cc85cc6545185 | |
parent | 514fff2b7c1f2e25e5c0611b598797f32aff1042 (diff) | |
parent | 22188e6ab4f02f981de3b9f228a40b1e8f98e6a1 (diff) |
Notes
587 files changed, 187752 insertions, 16595 deletions
diff --git a/contrib/hostapd/COPYING b/COPYING index 14f5453722a85..14f5453722a85 100644 --- a/contrib/hostapd/COPYING +++ b/COPYING diff --git a/README b/README new file mode 100644 index 0000000000000..9c6be85d9ea3a --- /dev/null +++ b/README @@ -0,0 +1,19 @@ +wpa_supplicant and hostapd v0.6.x +--------------------------------- + +Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +These program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + +This package may include either wpa_supplicant, hostapd, or both. See +README file respective subdirectories (wpa_supplicant/README or +hostapd/README) for more details. + +Source code files have been moved around in v0.6.x releases and +compared to earlier releases, the programs are now build by first +going to a subdirectory (wpa_supplicant or hostapd) and creating +build configuration (.config) and running 'make' there (for +Linux/BSD/cygwin builds). diff --git a/contrib/hostapd/FREEBSD-Xlist b/contrib/hostapd/FREEBSD-Xlist deleted file mode 100644 index 1f544522a1b5a..0000000000000 --- a/contrib/hostapd/FREEBSD-Xlist +++ /dev/null @@ -1,17 +0,0 @@ -$FreeBSD$ -.cvsignore -driver.c -driver_bsd.c -driver_devicescape.c -driver_madwifi.c -driver_prism54.c -l2_packet_freebsd.c -l2_packet_linux.c -l2_packet_ndis.c -l2_packet_pcap.c -l2_packet_winpcap.c -nt_password_hash.c -os_win32.c -prism54.h -priv_netlink.h -wireless_copy.h diff --git a/contrib/hostapd/FREEBSD-upgrade b/contrib/hostapd/FREEBSD-upgrade deleted file mode 100644 index 08922051fe55a..0000000000000 --- a/contrib/hostapd/FREEBSD-upgrade +++ /dev/null @@ -1,24 +0,0 @@ -$FreeBSD$ - -WPA/802.1x Authenticator - originals can be found at: http://hostap.epitest.fi/releases/ - - -For the import files and directories were pruned by: - - tar -X FREEBSD-Xlist -zxf hostapd-0.5.8.tar.gz - -then imported by: - - cvs import -m 'Import of hostapd 0.5.8' \ - src/contrib/hostapd MALINEN v0_5_8 - -To make local changes to hostapd, simply patch and commit to the -main branch (aka HEAD). Never make local changes on the vendor -(MALINEN) branch. - -All local changes should be submitted to Jouni Malinen for inclusion in -the next vendor release. - -sam@FreeBSD.org -7-July-2007 diff --git a/contrib/hostapd/Makefile b/contrib/hostapd/Makefile deleted file mode 100644 index 4fadede020574..0000000000000 --- a/contrib/hostapd/Makefile +++ /dev/null @@ -1,444 +0,0 @@ -CC=gcc -DIR_WPA_SUPPLICANT=. - -ifndef CFLAGS -CFLAGS = -MMD -O2 -Wall -g -endif - -# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to -# a file (undefine it, if you want to save in binary size) -CFLAGS += -DHOSTAPD_DUMP_STATE - -# Include directories for CVS version -CFLAGS += -I. -I../utils -I$(DIR_WPA_SUPPLICANT) - -# Uncomment following line and set the path to your kernel tree include -# directory if your C library does not include all header files. -# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include - --include .config - -ifndef CONFIG_OS -ifdef CONFIG_NATIVE_WINDOWS -CONFIG_OS=win32 -else -CONFIG_OS=unix -endif -endif - -ifeq ($(CONFIG_OS), internal) -CFLAGS += -DOS_NO_C_LIB_DEFINES -endif - -ifdef CONFIG_NATIVE_WINDOWS -CFLAGS += -DCONFIG_NATIVE_WINDOWS -LIBS += -lws2_32 -endif - -OBJS = hostapd.o eloop.o ieee802_1x.o eapol_sm.o radius.o md5.o rc4.o md4.o \ - common.o ieee802_11.o config.o ieee802_11_auth.o accounting.o \ - sta_info.o radius_client.o sha1.o wpa.o aes_wrap.o ctrl_iface.o \ - driver_conf.o os_$(CONFIG_OS).o preauth.o pmksa_cache.o beacon.o \ - hw_features.o wme.o ap_list.o reconfig.o \ - mlme.o vlan_init.o ieee802_11h.o - -HOBJS=hlr_auc_gw.o common.o os_$(CONFIG_OS).o milenage.o aes_wrap.o - -CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX - -ifdef CONFIG_IAPP -CFLAGS += -DCONFIG_IAPP -OBJS += iapp.o -endif - -ifdef CONFIG_RSN_PREAUTH -CFLAGS += -DCONFIG_RSN_PREAUTH -CONFIG_L2_PACKET=y -endif - -ifdef CONFIG_PEERKEY -CFLAGS += -DCONFIG_PEERKEY -endif - -ifdef CONFIG_IEEE80211W -CFLAGS += -DCONFIG_IEEE80211W -NEED_SHA256=y -endif - -ifdef CONFIG_DRIVER_HOSTAP -CFLAGS += -DCONFIG_DRIVER_HOSTAP -OBJS += driver.o -endif - -ifdef CONFIG_DRIVER_WIRED -CFLAGS += -DCONFIG_DRIVER_WIRED -OBJS += driver_wired.o -endif - -ifdef CONFIG_DRIVER_MADWIFI -CFLAGS += -DCONFIG_DRIVER_MADWIFI -OBJS += driver_madwifi.o -CONFIG_L2_PACKET=y -endif - -ifdef CONFIG_DRIVER_PRISM54 -CFLAGS += -DCONFIG_DRIVER_PRISM54 -OBJS += driver_prism54.o -endif - -ifdef CONFIG_DRIVER_DEVICESCAPE -CFLAGS += -DCONFIG_DRIVER_DEVICESCAPE -OBJS += driver_devicescape.o -endif - -ifdef CONFIG_DRIVER_BSD -CFLAGS += -DCONFIG_DRIVER_BSD -OBJS += driver_bsd.o -CONFIG_L2_PACKET=y -CONFIG_DNET_PCAP=y -CONFIG_L2_FREEBSD=y -endif - -ifdef CONFIG_DRIVER_TEST -CFLAGS += -DCONFIG_DRIVER_TEST -OBJS += driver_test.o -endif - -ifdef CONFIG_L2_PACKET -ifdef CONFIG_DNET_PCAP -ifdef CONFIG_L2_FREEBSD -LIBS += -lpcap -OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_freebsd.o -else -LIBS += -ldnet -lpcap -OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_pcap.o -endif -else -OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_linux.o -endif -endif - - -ifdef CONFIG_EAP_MD5 -CFLAGS += -DEAP_MD5 -OBJS += eap_md5.o -endif - -ifdef CONFIG_EAP_TLS -CFLAGS += -DEAP_TLS -OBJS += eap_tls.o -TLS_FUNCS=y -endif - -ifdef CONFIG_EAP_PEAP -CFLAGS += -DEAP_PEAP -OBJS += eap_peap.o -TLS_FUNCS=y -CONFIG_EAP_TLV=y -CONFIG_EAP_MSCHAPV2=y -endif - -ifdef CONFIG_EAP_TTLS -CFLAGS += -DEAP_TTLS -OBJS += eap_ttls.o -TLS_FUNCS=y -endif - -ifdef CONFIG_EAP_MSCHAPV2 -CFLAGS += -DEAP_MSCHAPv2 -OBJS += eap_mschapv2.o -MS_FUNCS=y -endif - -ifdef CONFIG_EAP_GTC -CFLAGS += -DEAP_GTC -OBJS += eap_gtc.o -endif - -ifdef CONFIG_EAP_SIM -CFLAGS += -DEAP_SIM -OBJS += eap_sim.o -CONFIG_EAP_SIM_COMMON=y -endif - -ifdef CONFIG_EAP_AKA -CFLAGS += -DEAP_AKA -OBJS += eap_aka.o -CONFIG_EAP_SIM_COMMON=y -endif - -ifdef CONFIG_EAP_SIM_COMMON -OBJS += $(DIR_WPA_SUPPLICANT)/eap_sim_common.o -# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be -# replaced with another file implementating the interface specified in -# eap_sim_db.h. -OBJS += eap_sim_db.o -endif - -ifdef CONFIG_EAP_PAX -CFLAGS += -DEAP_PAX -OBJS += eap_pax.o $(DIR_WPA_SUPPLICANT)/eap_pax_common.o -endif - -ifdef CONFIG_EAP_PSK -CFLAGS += -DEAP_PSK -OBJS += eap_psk.o $(DIR_WPA_SUPPLICANT)/eap_psk_common.o -endif - -ifdef CONFIG_EAP_SAKE -CFLAGS += -DEAP_SAKE -OBJS += eap_sake.o $(DIR_WPA_SUPPLICANT)/eap_sake_common.o -endif - -ifdef CONFIG_EAP_GPSK -CFLAGS += -DEAP_GPSK -OBJS += eap_gpsk.o $(DIR_WPA_SUPPLICANT)/eap_gpsk_common.o -ifdef CONFIG_EAP_GPSK_SHA256 -CFLAGS += -DEAP_GPSK_SHA256 -NEED_SHA256=y -endif -endif - -ifdef CONFIG_EAP_VENDOR_TEST -CFLAGS += -DEAP_VENDOR_TEST -OBJS += eap_vendor_test.o -endif - -ifdef CONFIG_EAP_TLV -CFLAGS += -DEAP_TLV -OBJS += eap_tlv.o -endif - -ifdef CONFIG_EAP -CFLAGS += -DEAP_SERVER -OBJS += eap.o eap_methods.o eap_identity.o -endif - -ifndef CONFIG_TLS -CONFIG_TLS=openssl -endif - -ifdef TLS_FUNCS -# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) -CFLAGS += -DEAP_TLS_FUNCS -OBJS += eap_tls_common.o -ifeq ($(CONFIG_TLS), openssl) -OBJS += $(DIR_WPA_SUPPLICANT)/tls_openssl.o -LIBS += -lssl -lcrypto -LIBS_p += -lcrypto -LIBS_h += -lcrypto -endif -ifeq ($(CONFIG_TLS), gnutls) -OBJS += $(DIR_WPA_SUPPLICANT)/tls_gnutls.o -LIBS += -lgnutls -lgcrypt -lgpg-error -LIBS_p += -lgcrypt -LIBS_h += -lgcrypt -endif -ifdef CONFIG_GNUTLS_EXTRA -CFLAGS += -DCONFIG_GNUTLS_EXTRA -LIBS += -lgnutls-extra -endif -NEED_CRYPTO=y -else -OBJS += $(DIR_WPA_SUPPLICANT)/tls_none.o -endif - -ifdef CONFIG_PKCS12 -CFLAGS += -DPKCS12_FUNCS -endif - -ifdef MS_FUNCS -OBJS += $(DIR_WPA_SUPPLICANT)/ms_funcs.o -NEED_CRYPTO=y -endif - -ifdef NEED_CRYPTO -ifndef TLS_FUNCS -ifeq ($(CONFIG_TLS), openssl) -LIBS += -lcrypto -LIBS_p += -lcrypto -LIBS_h += -lcrypto -endif -ifeq ($(CONFIG_TLS), gnutls) -LIBS += -lgcrypt -LIBS_p += -lgcrypt -LIBS_h += -lgcrypt -endif -endif -ifeq ($(CONFIG_TLS), openssl) -OBJS += $(DIR_WPA_SUPPLICANT)/crypto.o -OBJS_p += $(DIR_WPA_SUPPLICANT)/crypto.o -HOBJS += $(DIR_WPA_SUPPLICANT)/crypto.o -CONFIG_INTERNAL_SHA256=y -endif -ifeq ($(CONFIG_TLS), gnutls) -OBJS += $(DIR_WPA_SUPPLICANT)/crypto_gnutls.o -OBJS_p += $(DIR_WPA_SUPPLICANT)/crypto_gnutls.o -HOBJS += $(DIR_WPA_SUPPLICANT)/crypto_gnutls.o -CONFIG_INTERNAL_SHA256=y -endif -else -CONFIG_INTERNAL_AES=y -CONFIG_INTERNAL_SHA1=y -CONFIG_INTERNAL_MD5=y -CONFIG_INTERNAL_SHA256=y -endif - -ifdef CONFIG_INTERNAL_AES -CFLAGS += -DINTERNAL_AES -endif -ifdef CONFIG_INTERNAL_SHA1 -CFLAGS += -DINTERNAL_SHA1 -endif -ifdef CONFIG_INTERNAL_SHA256 -CFLAGS += -DINTERNAL_SHA256 -endif -ifdef CONFIG_INTERNAL_MD5 -CFLAGS += -DINTERNAL_MD5 -endif -ifdef CONFIG_INTERNAL_MD4 -CFLAGS += -DINTERNAL_MD4 -endif - -ifdef NEED_SHA256 -OBJS += sha256.o -endif - -ifdef CONFIG_RADIUS_SERVER -CFLAGS += -DRADIUS_SERVER -OBJS += radius_server.o -endif - -ifdef CONFIG_IPV6 -CFLAGS += -DCONFIG_IPV6 -endif - -ifdef CONFIG_DRIVER_RADIUS_ACL -CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL -endif - -ifdef CONFIG_FULL_DYNAMIC_VLAN -# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges -# and vlan interfaces for the vlan feature. -CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN -endif - -ALL=hostapd hostapd_cli - -all: verify_config $(ALL) - -verify_config: - @if [ ! -r .config ]; then \ - echo 'Building hostapd requires a configuration file'; \ - echo '(.config). See README for more instructions. You can'; \ - echo 'run "cp defconfig .config" to create an example'; \ - echo 'configuration.'; \ - exit 1; \ - fi - -install: all - for i in $(ALL); do cp $$i /usr/local/bin/$$i; done - -hostapd: $(OBJS) - $(CC) -o hostapd $(OBJS) $(LIBS) - -driver_conf.c: Makefile .config - rm -f driver_conf.c - echo '/* THIS FILE AUTOMATICALLY GENERATED, DO NOT EDIT! */' \ - > driver_conf.c - echo '#include "includes.h"' >> driver_conf.c - echo '#include "hostapd.h"' >> driver_conf.c - echo '#include "driver.h"' >> driver_conf.c -ifdef CONFIG_DRIVER_HOSTAP - echo "void hostap_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_WIRED - echo "void wired_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_MADWIFI - echo "void madwifi_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_PRISM54 - echo "void prism54_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_DEVICESCAPE - echo "void devicescape_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_BSD - echo "void bsd_driver_register(void);" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_TEST - echo "void test_driver_register(void);" >> driver_conf.c -endif - echo 'void register_drivers(void) {' >> driver_conf.c -ifdef CONFIG_DRIVER_HOSTAP - echo "hostap_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_WIRED - echo "wired_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_MADWIFI - echo "madwifi_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_PRISM54 - echo "prism54_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_DEVICESCAPE - echo "devicescape_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_BSD - echo "bsd_driver_register();" >> driver_conf.c -endif -ifdef CONFIG_DRIVER_TEST - echo "test_driver_register();" >> driver_conf.c -endif - echo '}' >> driver_conf.c - -hostapd_cli: hostapd_cli.o $(DIR_WPA_SUPPLICANT)/wpa_ctrl.o - $(CC) -o hostapd_cli hostapd_cli.o $(DIR_WPA_SUPPLICANT)/wpa_ctrl.o - -NOBJS = nt_password_hash.o $(DIR_WPA_SUPPLICANT)/ms_funcs.o sha1.o rc4.o md5.o -NOBJS += $(DIR_WPA_SUPPLICANT)/crypto.o os_$(CONFIG_OS).o -ifdef TLS_FUNCS -LIBS_n += -lcrypto -endif - -nt_password_hash: $(NOBJS) - $(CC) -o nt_password_hash $(NOBJS) $(LIBS_n) - -hlr_auc_gw: $(HOBJS) - $(CC) -o hlr_auc_gw $(HOBJS) $(LIBS_h) - -clean: - rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw - rm -f *.d driver_conf.c - -%.eps: %.fig - fig2dev -L eps $*.fig $*.eps - -%.png: %.fig - fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \ - > $*.png - -docs-pics: doc/hostapd.png doc/hostapd.eps - -docs: docs-pics - doxygen doc/doxygen.full - $(MAKE) -C doc/latex - cp doc/latex/refman.pdf hostapd-devel.pdf - -docs-fast: docs-pics - doxygen doc/doxygen.fast - -clean-docs: - rm -rf doc/latex doc/html - rm -f doc/hosta.d{eps,png} hostapd-devel.pdf - -TEST_SRC_MILENAGE = milenage.c aes_wrap.c common.c os_$(CONFIG_OS).c -test-milenage: $(TEST_SRC_MILENAGE) - $(CC) -o test-milenage -Wall -Werror $(TEST_SRC_MILENAGE) \ - -DTEST_MAIN_MILENAGE -I. -I../wpa_supplicant -DINTERNAL_AES - ./test-milenage - rm test-milenage - --include $(OBJS:%.o=%.d) diff --git a/contrib/hostapd/aes_wrap.h b/contrib/hostapd/aes_wrap.h deleted file mode 100644 index 5eb4342a5bf97..0000000000000 --- a/contrib/hostapd/aes_wrap.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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-2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef AES_WRAP_H -#define AES_WRAP_H - -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_vector(const u8 *key, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -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); -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/hostapd/config.c b/contrib/hostapd/config.c deleted file mode 100644 index d1b2ba3fa5e0a..0000000000000 --- a/contrib/hostapd/config.c +++ /dev/null @@ -1,1994 +0,0 @@ -/* - * hostapd / Configuration file - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#ifndef CONFIG_NATIVE_WINDOWS -#include <grp.h> -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "hostapd.h" -#include "driver.h" -#include "sha1.h" -#include "eap.h" -#include "radius_client.h" -#include "wpa_common.h" - - -#define MAX_STA_COUNT 2007 - - -static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, - const char *fname) -{ - FILE *f; - char buf[128], *pos, *pos2; - int line = 0, vlan_id; - struct hostapd_vlan *vlan; - - f = fopen(fname, "r"); - if (!f) { - printf("VLAN file '%s' not readable.\n", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (buf[0] == '*') { - vlan_id = VLAN_ID_WILDCARD; - pos = buf + 1; - } else { - vlan_id = strtol(buf, &pos, 10); - if (buf == pos || vlan_id < 1 || - vlan_id > MAX_VLAN_ID) { - printf("Invalid VLAN ID at line %d in '%s'\n", - line, fname); - fclose(f); - return -1; - } - } - - while (*pos == ' ' || *pos == '\t') - pos++; - pos2 = pos; - while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') - pos2++; - *pos2 = '\0'; - if (*pos == '\0' || strlen(pos) > IFNAMSIZ) { - printf("Invalid VLAN ifname at line %d in '%s'\n", - line, fname); - fclose(f); - return -1; - } - - vlan = malloc(sizeof(*vlan)); - if (vlan == NULL) { - printf("Out of memory while reading VLAN interfaces " - "from '%s'\n", fname); - fclose(f); - return -1; - } - - memset(vlan, 0, sizeof(*vlan)); - vlan->vlan_id = vlan_id; - strncpy(vlan->ifname, pos, sizeof(vlan->ifname)); - if (bss->vlan_tail) - bss->vlan_tail->next = vlan; - else - bss->vlan = vlan; - bss->vlan_tail = vlan; - } - - fclose(f); - - return 0; -} - - -static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) -{ - struct hostapd_vlan *vlan, *prev; - - vlan = bss->vlan; - prev = NULL; - while (vlan) { - prev = vlan; - vlan = vlan->next; - free(prev); - } - - bss->vlan = NULL; -} - - -/* convert floats with one decimal place to value*10 int, i.e., - * "1.5" will return 15 */ -static int hostapd_config_read_int10(const char *value) -{ - int i, d; - char *pos; - - i = atoi(value); - pos = strchr(value, '.'); - d = 0; - if (pos) { - pos++; - if (*pos >= '0' && *pos <= '9') - d = *pos - '0'; - } - - return i * 10 + d; -} - - -static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) -{ - bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; - bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; - bss->logger_syslog = (unsigned int) -1; - bss->logger_stdout = (unsigned int) -1; - - bss->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY; - - bss->wep_rekeying_period = 300; - /* use key0 in individual key and key1 in broadcast key */ - bss->broadcast_key_idx_min = 1; - bss->broadcast_key_idx_max = 2; - bss->eap_reauth_period = 3600; - - bss->wpa_group_rekey = 600; - bss->wpa_gmk_rekey = 86400; - bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - bss->wpa_pairwise = WPA_CIPHER_TKIP; - bss->wpa_group = WPA_CIPHER_TKIP; - - bss->max_num_sta = MAX_STA_COUNT; - - bss->dtim_period = 2; - - bss->radius_server_auth_port = 1812; - bss->ap_max_inactivity = AP_MAX_INACTIVITY; - bss->eapol_version = EAPOL_VERSION; -} - - -static struct hostapd_config * hostapd_config_defaults(void) -{ - struct hostapd_config *conf; - struct hostapd_bss_config *bss; - int i; - const int aCWmin = 15, aCWmax = 1024; - const struct hostapd_wme_ac_params ac_bk = - { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ - const struct hostapd_wme_ac_params ac_be = - { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ - const struct hostapd_wme_ac_params ac_vi = /* video traffic */ - { aCWmin >> 1, aCWmin, 2, 3000 / 32, 1 }; - const struct hostapd_wme_ac_params ac_vo = /* voice traffic */ - { aCWmin >> 2, aCWmin >> 1, 2, 1500 / 32, 1 }; - - conf = wpa_zalloc(sizeof(*conf)); - bss = wpa_zalloc(sizeof(*bss)); - if (conf == NULL || bss == NULL) { - printf("Failed to allocate memory for configuration data.\n"); - free(conf); - free(bss); - return NULL; - } - - /* set default driver based on configuration */ - conf->driver = driver_lookup("default"); - if (conf->driver == NULL) { - printf("No default driver registered!\n"); - free(conf); - free(bss); - return NULL; - } - - bss->radius = wpa_zalloc(sizeof(*bss->radius)); - if (bss->radius == NULL) { - free(conf); - free(bss); - return NULL; - } - - hostapd_config_defaults_bss(bss); - - conf->num_bss = 1; - conf->bss = bss; - - conf->beacon_int = 100; - conf->rts_threshold = -1; /* use driver default: 2347 */ - conf->fragm_threshold = -1; /* user driver default: 2346 */ - conf->send_probe_response = 1; - conf->bridge_packets = INTERNAL_BRIDGE_DO_NOT_CONTROL; - - memcpy(conf->country, "US ", 3); - - for (i = 0; i < NUM_TX_QUEUES; i++) - conf->tx_queue[i].aifs = -1; /* use hw default */ - - conf->wme_ac_params[0] = ac_be; - conf->wme_ac_params[1] = ac_bk; - conf->wme_ac_params[2] = ac_vi; - conf->wme_ac_params[3] = ac_vo; - - return conf; -} - - -static int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) -{ - if (inet_aton(txt, &addr->u.v4)) { - addr->af = AF_INET; - return 0; - } - -#ifdef CONFIG_IPV6 - if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { - addr->af = AF_INET6; - return 0; - } -#endif /* CONFIG_IPV6 */ - - return -1; -} - - -int hostapd_mac_comp(const void *a, const void *b) -{ - return memcmp(a, b, sizeof(macaddr)); -} - - -int hostapd_mac_comp_empty(const void *a) -{ - macaddr empty = { 0 }; - return memcmp(a, empty, sizeof(macaddr)); -} - - -static int hostapd_config_read_maclist(const char *fname, macaddr **acl, - int *num) -{ - FILE *f; - char buf[128], *pos; - int line = 0; - u8 addr[ETH_ALEN]; - macaddr *newacl; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("MAC list file '%s' not found.\n", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (hwaddr_aton(buf, addr)) { - printf("Invalid MAC address '%s' at line %d in '%s'\n", - buf, line, fname); - fclose(f); - return -1; - } - - newacl = (macaddr *) realloc(*acl, (*num + 1) * ETH_ALEN); - if (newacl == NULL) { - printf("MAC list reallocation failed\n"); - fclose(f); - return -1; - } - - *acl = newacl; - memcpy((*acl)[*num], addr, ETH_ALEN); - (*num)++; - } - - fclose(f); - - qsort(*acl, *num, sizeof(macaddr), hostapd_mac_comp); - - return 0; -} - - -static int hostapd_config_read_wpa_psk(const char *fname, - struct hostapd_ssid *ssid) -{ - FILE *f; - char buf[128], *pos; - int line = 0, ret = 0, len, ok; - u8 addr[ETH_ALEN]; - struct hostapd_wpa_psk *psk; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("WPA PSK file '%s' not found.\n", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (hwaddr_aton(buf, addr)) { - printf("Invalid MAC address '%s' on line %d in '%s'\n", - buf, line, fname); - ret = -1; - break; - } - - psk = wpa_zalloc(sizeof(*psk)); - if (psk == NULL) { - printf("WPA PSK allocation failed\n"); - ret = -1; - break; - } - if (memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) - psk->group = 1; - else - memcpy(psk->addr, addr, ETH_ALEN); - - pos = buf + 17; - if (pos == '\0') { - printf("No PSK on line %d in '%s'\n", line, fname); - free(psk); - ret = -1; - break; - } - pos++; - - ok = 0; - len = strlen(pos); - if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) - ok = 1; - else if (len >= 8 && len < 64) { - pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, - 4096, psk->psk, PMK_LEN); - ok = 1; - } - if (!ok) { - printf("Invalid PSK '%s' on line %d in '%s'\n", - pos, line, fname); - free(psk); - ret = -1; - break; - } - - psk->next = ssid->wpa_psk; - ssid->wpa_psk = psk; - } - - fclose(f); - - return ret; -} - - -int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) -{ - struct hostapd_ssid *ssid = &conf->ssid; - - if (ssid->wpa_passphrase != NULL) { - if (ssid->wpa_psk != NULL) { - printf("Warning: both WPA PSK and passphrase set. " - "Using passphrase.\n"); - free(ssid->wpa_psk); - } - ssid->wpa_psk = wpa_zalloc(sizeof(struct hostapd_wpa_psk)); - if (ssid->wpa_psk == NULL) { - printf("Unable to alloc space for PSK\n"); - return -1; - } - wpa_hexdump_ascii(MSG_DEBUG, "SSID", - (u8 *) ssid->ssid, ssid->ssid_len); - wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", - (u8 *) ssid->wpa_passphrase, - strlen(ssid->wpa_passphrase)); - pbkdf2_sha1(ssid->wpa_passphrase, - ssid->ssid, ssid->ssid_len, - 4096, ssid->wpa_psk->psk, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", - ssid->wpa_psk->psk, PMK_LEN); - ssid->wpa_psk->group = 1; - - memset(ssid->wpa_passphrase, 0, - strlen(ssid->wpa_passphrase)); - free(ssid->wpa_passphrase); - ssid->wpa_passphrase = NULL; - } - - if (ssid->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, - &conf->ssid)) - return -1; - free(ssid->wpa_psk_file); - ssid->wpa_psk_file = NULL; - } - - return 0; -} - - -#ifdef EAP_SERVER -static int hostapd_config_read_eap_user(const char *fname, - struct hostapd_bss_config *conf) -{ - FILE *f; - char buf[512], *pos, *start, *pos2; - int line = 0, ret = 0, num_methods; - struct hostapd_eap_user *user, *tail = NULL; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("EAP user file '%s' not found.\n", fname); - return -1; - } - - /* Lines: "user" METHOD,METHOD2 "password" (password optional) */ - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - user = NULL; - - if (buf[0] != '"' && buf[0] != '*') { - printf("Invalid EAP identity (no \" in start) on " - "line %d in '%s'\n", line, fname); - goto failed; - } - - user = wpa_zalloc(sizeof(*user)); - if (user == NULL) { - printf("EAP user allocation failed\n"); - goto failed; - } - user->force_version = -1; - - if (buf[0] == '*') { - pos = buf; - } else { - pos = buf + 1; - start = pos; - while (*pos != '"' && *pos != '\0') - pos++; - if (*pos == '\0') { - printf("Invalid EAP identity (no \" in end) on" - " line %d in '%s'\n", line, fname); - goto failed; - } - - user->identity = malloc(pos - start); - if (user->identity == NULL) { - printf("Failed to allocate memory for EAP " - "identity\n"); - goto failed; - } - memcpy(user->identity, start, pos - start); - user->identity_len = pos - start; - - if (pos[0] == '"' && pos[1] == '*') { - user->wildcard_prefix = 1; - pos++; - } - } - pos++; - while (*pos == ' ' || *pos == '\t') - pos++; - - if (*pos == '\0') { - printf("No EAP method on line %d in '%s'\n", - line, fname); - goto failed; - } - - start = pos; - while (*pos != ' ' && *pos != '\t' && *pos != '\0') - pos++; - if (*pos == '\0') { - pos = NULL; - } else { - *pos = '\0'; - pos++; - } - num_methods = 0; - while (*start) { - char *pos3 = strchr(start, ','); - if (pos3) { - *pos3++ = '\0'; - } - user->methods[num_methods].method = - eap_get_type(start, &user->methods[num_methods] - .vendor); - if (user->methods[num_methods].vendor == - EAP_VENDOR_IETF && - user->methods[num_methods].method == EAP_TYPE_NONE) - { - printf("Unsupported EAP type '%s' on line %d " - "in '%s'\n", start, line, fname); - goto failed; - } - - num_methods++; - if (num_methods >= EAP_USER_MAX_METHODS) - break; - if (pos3 == NULL) - break; - start = pos3; - } - if (num_methods == 0) { - printf("No EAP types configured on line %d in '%s'\n", - line, fname); - goto failed; - } - - if (pos == NULL) - goto done; - - while (*pos == ' ' || *pos == '\t') - pos++; - if (*pos == '\0') - goto done; - - if (strncmp(pos, "[ver=0]", 7) == 0) { - user->force_version = 0; - goto done; - } - - if (strncmp(pos, "[ver=1]", 7) == 0) { - user->force_version = 1; - goto done; - } - - if (strncmp(pos, "[2]", 3) == 0) { - user->phase2 = 1; - goto done; - } - - if (*pos == '"') { - pos++; - start = pos; - while (*pos != '"' && *pos != '\0') - pos++; - if (*pos == '\0') { - printf("Invalid EAP password (no \" in end) " - "on line %d in '%s'\n", line, fname); - goto failed; - } - - user->password = malloc(pos - start); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP " - "password\n"); - goto failed; - } - memcpy(user->password, start, pos - start); - user->password_len = pos - start; - - pos++; - } else if (strncmp(pos, "hash:", 5) == 0) { - pos += 5; - pos2 = pos; - while (*pos2 != '\0' && *pos2 != ' ' && - *pos2 != '\t' && *pos2 != '#') - pos2++; - if (pos2 - pos != 32) { - printf("Invalid password hash on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password = malloc(16); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP " - "password hash\n"); - goto failed; - } - if (hexstr2bin(pos, user->password, 16) < 0) { - printf("Invalid hash password on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password_len = 16; - user->password_hash = 1; - pos = pos2; - } else { - pos2 = pos; - while (*pos2 != '\0' && *pos2 != ' ' && - *pos2 != '\t' && *pos2 != '#') - pos2++; - if ((pos2 - pos) & 1) { - printf("Invalid hex password on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password = malloc((pos2 - pos) / 2); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP " - "password\n"); - goto failed; - } - if (hexstr2bin(pos, user->password, - (pos2 - pos) / 2) < 0) { - printf("Invalid hex password on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password_len = (pos2 - pos) / 2; - pos = pos2; - } - - while (*pos == ' ' || *pos == '\t') - pos++; - if (strncmp(pos, "[2]", 3) == 0) { - user->phase2 = 1; - } - - done: - if (tail == NULL) { - tail = conf->eap_user = user; - } else { - tail->next = user; - tail = user; - } - continue; - - failed: - if (user) { - free(user->password); - free(user->identity); - free(user); - } - ret = -1; - break; - } - - fclose(f); - - return ret; -} -#endif /* EAP_SERVER */ - - -static int -hostapd_config_read_radius_addr(struct hostapd_radius_server **server, - int *num_server, const char *val, int def_port, - struct hostapd_radius_server **curr_serv) -{ - struct hostapd_radius_server *nserv; - int ret; - static int server_index = 1; - - nserv = realloc(*server, (*num_server + 1) * sizeof(*nserv)); - if (nserv == NULL) - return -1; - - *server = nserv; - nserv = &nserv[*num_server]; - (*num_server)++; - (*curr_serv) = nserv; - - memset(nserv, 0, sizeof(*nserv)); - nserv->port = def_port; - ret = hostapd_parse_ip_addr(val, &nserv->addr); - nserv->index = server_index++; - - return ret; -} - - -static int hostapd_config_parse_key_mgmt(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (strcmp(start, "WPA-PSK") == 0) - val |= WPA_KEY_MGMT_PSK; - else if (strcmp(start, "WPA-EAP") == 0) - val |= WPA_KEY_MGMT_IEEE8021X; - else { - printf("Line %d: invalid key_mgmt '%s'\n", - line, start); - free(buf); - return -1; - } - - if (last) - break; - start = end + 1; - } - - free(buf); - if (val == 0) { - printf("Line %d: no key_mgmt values configured.\n", line); - return -1; - } - - return val; -} - - -static int hostapd_config_parse_cipher(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (strcmp(start, "CCMP") == 0) - val |= WPA_CIPHER_CCMP; - else if (strcmp(start, "TKIP") == 0) - val |= WPA_CIPHER_TKIP; - else if (strcmp(start, "WEP104") == 0) - val |= WPA_CIPHER_WEP104; - else if (strcmp(start, "WEP40") == 0) - val |= WPA_CIPHER_WEP40; - else if (strcmp(start, "NONE") == 0) - val |= WPA_CIPHER_NONE; - else { - printf("Line %d: invalid cipher '%s'.", line, start); - free(buf); - return -1; - } - - if (last) - break; - start = end + 1; - } - free(buf); - - if (val == 0) { - printf("Line %d: no cipher values configured.", line); - return -1; - } - return val; -} - - -static int hostapd_config_check_bss(struct hostapd_bss_config *bss, - struct hostapd_config *conf) -{ - if (bss->ieee802_1x && !bss->eap_server && - !bss->radius->auth_servers) { - printf("Invalid IEEE 802.1X configuration (no EAP " - "authenticator configured).\n"); - return -1; - } - - if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && - bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && - bss->ssid.wpa_psk_file == NULL) { - printf("WPA-PSK enabled, but PSK or passphrase is not " - "configured.\n"); - return -1; - } - - if (hostapd_mac_comp_empty(bss->bssid) != 0) { - size_t i; - - for (i = 0; i < conf->num_bss; i++) { - if ((&conf->bss[i] != bss) && - (hostapd_mac_comp(conf->bss[i].bssid, - bss->bssid) == 0)) { - printf("Duplicate BSSID " MACSTR - " on interface '%s' and '%s'.\n", - MAC2STR(bss->bssid), - conf->bss[i].iface, bss->iface); - return -1; - } - } - } - - return 0; -} - - -static int hostapd_config_check(struct hostapd_config *conf) -{ - size_t i; - - for (i = 0; i < conf->num_bss; i++) { - if (hostapd_config_check_bss(&conf->bss[i], conf)) - return -1; - } - - return 0; -} - - -static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, - char *val) -{ - size_t len = strlen(val); - - if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) - return -1; - - if (val[0] == '"') { - if (len < 2 || val[len - 1] != '"') - return -1; - len -= 2; - wep->key[keyidx] = malloc(len); - if (wep->key[keyidx] == NULL) - return -1; - memcpy(wep->key[keyidx], val + 1, len); - wep->len[keyidx] = len; - } else { - if (len & 1) - return -1; - len /= 2; - wep->key[keyidx] = malloc(len); - if (wep->key[keyidx] == NULL) - return -1; - wep->len[keyidx] = len; - if (hexstr2bin(val, wep->key[keyidx], len) < 0) - return -1; - } - - wep->keys_set++; - - return 0; -} - - -static int hostapd_parse_rates(int **rate_list, char *val) -{ - int *list; - int count; - char *pos, *end; - - free(*rate_list); - *rate_list = NULL; - - pos = val; - count = 0; - while (*pos != '\0') { - if (*pos == ' ') - count++; - pos++; - } - - list = malloc(sizeof(int) * (count + 2)); - if (list == NULL) - return -1; - pos = val; - count = 0; - while (*pos != '\0') { - end = strchr(pos, ' '); - if (end) - *end = '\0'; - - list[count++] = atoi(pos); - if (!end) - break; - pos = end + 1; - } - list[count] = -1; - - *rate_list = list; - return 0; -} - - -static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) -{ - struct hostapd_bss_config *bss; - - if (*ifname == '\0') - return -1; - - bss = realloc(conf->bss, (conf->num_bss + 1) * - sizeof(struct hostapd_bss_config)); - if (bss == NULL) { - printf("Failed to allocate memory for multi-BSS entry\n"); - return -1; - } - conf->bss = bss; - - bss = &(conf->bss[conf->num_bss]); - memset(bss, 0, sizeof(*bss)); - bss->radius = wpa_zalloc(sizeof(*bss->radius)); - if (bss->radius == NULL) { - printf("Failed to allocate memory for multi-BSS RADIUS " - "data\n"); - return -1; - } - - conf->num_bss++; - conf->last_bss = bss; - - hostapd_config_defaults_bss(bss); - snprintf(bss->iface, sizeof(bss->iface), "%s", ifname); - memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1); - - return 0; -} - - -static int valid_cw(int cw) -{ - return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || - cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); -} - - -enum { - IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ - IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ - IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ - IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */ - IEEE80211_TX_QUEUE_DATA4 = 4, - IEEE80211_TX_QUEUE_AFTER_BEACON = 6, - IEEE80211_TX_QUEUE_BEACON = 7 -}; - -static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, - char *val) -{ - int num; - char *pos; - struct hostapd_tx_queue_params *queue; - - /* skip 'tx_queue_' prefix */ - pos = name + 9; - if (strncmp(pos, "data", 4) == 0 && - pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { - num = pos[4] - '0'; - pos += 6; - } else if (strncmp(pos, "after_beacon_", 13) == 0) { - num = IEEE80211_TX_QUEUE_AFTER_BEACON; - pos += 13; - } else if (strncmp(pos, "beacon_", 7) == 0) { - num = IEEE80211_TX_QUEUE_BEACON; - pos += 7; - } else { - printf("Unknown tx_queue name '%s'\n", pos); - return -1; - } - - queue = &conf->tx_queue[num]; - - if (strcmp(pos, "aifs") == 0) { - queue->aifs = atoi(val); - if (queue->aifs < 0 || queue->aifs > 255) { - printf("Invalid AIFS value %d\n", queue->aifs); - return -1; - } - } else if (strcmp(pos, "cwmin") == 0) { - queue->cwmin = atoi(val); - if (!valid_cw(queue->cwmin)) { - printf("Invalid cwMin value %d\n", queue->cwmin); - return -1; - } - } else if (strcmp(pos, "cwmax") == 0) { - queue->cwmax = atoi(val); - if (!valid_cw(queue->cwmax)) { - printf("Invalid cwMax value %d\n", queue->cwmax); - return -1; - } - } else if (strcmp(pos, "burst") == 0) { - queue->burst = hostapd_config_read_int10(val); - } else { - printf("Unknown tx_queue field '%s'\n", pos); - return -1; - } - - queue->configured = 1; - - return 0; -} - - -static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, - char *val) -{ - int num, v; - char *pos; - struct hostapd_wme_ac_params *ac; - - /* skip 'wme_ac_' prefix */ - pos = name + 7; - if (strncmp(pos, "be_", 3) == 0) { - num = 0; - pos += 3; - } else if (strncmp(pos, "bk_", 3) == 0) { - num = 1; - pos += 3; - } else if (strncmp(pos, "vi_", 3) == 0) { - num = 2; - pos += 3; - } else if (strncmp(pos, "vo_", 3) == 0) { - num = 3; - pos += 3; - } else { - printf("Unknown wme name '%s'\n", pos); - return -1; - } - - ac = &conf->wme_ac_params[num]; - - if (strcmp(pos, "aifs") == 0) { - v = atoi(val); - if (v < 1 || v > 255) { - printf("Invalid AIFS value %d\n", v); - return -1; - } - ac->aifs = v; - } else if (strcmp(pos, "cwmin") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - printf("Invalid cwMin value %d\n", v); - return -1; - } - ac->cwmin = v; - } else if (strcmp(pos, "cwmax") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - printf("Invalid cwMax value %d\n", v); - return -1; - } - ac->cwmax = v; - } else if (strcmp(pos, "txop_limit") == 0) { - v = atoi(val); - if (v < 0 || v > 0xffff) { - printf("Invalid txop value %d\n", v); - return -1; - } - ac->txopLimit = v; - } else if (strcmp(pos, "acm") == 0) { - v = atoi(val); - if (v < 0 || v > 1) { - printf("Invalid acm value %d\n", v); - return -1; - } - ac->admission_control_mandatory = v; - } else { - printf("Unknown wme_ac_ field '%s'\n", pos); - return -1; - } - - return 0; -} - - -struct hostapd_config * hostapd_config_read(const char *fname) -{ - struct hostapd_config *conf; - struct hostapd_bss_config *bss; - FILE *f; - char buf[256], *pos; - int line = 0; - int errors = 0; - size_t i; - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open configuration file '%s' for reading.\n", - fname); - return NULL; - } - - conf = hostapd_config_defaults(); - if (conf == NULL) { - fclose(f); - return NULL; - } - bss = conf->last_bss = conf->bss; - - while (fgets(buf, sizeof(buf), f)) { - bss = conf->last_bss; - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - pos = strchr(buf, '='); - if (pos == NULL) { - printf("Line %d: invalid line '%s'\n", line, buf); - errors++; - continue; - } - *pos = '\0'; - pos++; - - if (strcmp(buf, "interface") == 0) { - snprintf(conf->bss[0].iface, - sizeof(conf->bss[0].iface), "%s", pos); - } else if (strcmp(buf, "bridge") == 0) { - snprintf(bss->bridge, sizeof(bss->bridge), "%s", pos); - } else if (strcmp(buf, "driver") == 0) { - conf->driver = driver_lookup(pos); - if (conf->driver == NULL) { - printf("Line %d: invalid/unknown driver " - "'%s'\n", line, pos); - errors++; - } - } else if (strcmp(buf, "debug") == 0) { - bss->debug = atoi(pos); - } else if (strcmp(buf, "logger_syslog_level") == 0) { - bss->logger_syslog_level = atoi(pos); - } else if (strcmp(buf, "logger_stdout_level") == 0) { - bss->logger_stdout_level = atoi(pos); - } else if (strcmp(buf, "logger_syslog") == 0) { - bss->logger_syslog = atoi(pos); - } else if (strcmp(buf, "logger_stdout") == 0) { - bss->logger_stdout = atoi(pos); - } else if (strcmp(buf, "dump_file") == 0) { - bss->dump_log_name = strdup(pos); - } else if (strcmp(buf, "ssid") == 0) { - bss->ssid.ssid_len = strlen(pos); - if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || - bss->ssid.ssid_len < 1) { - printf("Line %d: invalid SSID '%s'\n", line, - pos); - errors++; - } else { - memcpy(bss->ssid.ssid, pos, - bss->ssid.ssid_len); - bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; - bss->ssid.ssid_set = 1; - } - } else if (strcmp(buf, "macaddr_acl") == 0) { - bss->macaddr_acl = atoi(pos); - if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && - bss->macaddr_acl != DENY_UNLESS_ACCEPTED && - bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { - printf("Line %d: unknown macaddr_acl %d\n", - line, bss->macaddr_acl); - } - } else if (strcmp(buf, "accept_mac_file") == 0) { - if (hostapd_config_read_maclist(pos, &bss->accept_mac, - &bss->num_accept_mac)) - { - printf("Line %d: Failed to read " - "accept_mac_file '%s'\n", - line, pos); - errors++; - } - } else if (strcmp(buf, "deny_mac_file") == 0) { - if (hostapd_config_read_maclist(pos, &bss->deny_mac, - &bss->num_deny_mac)) - { - printf("Line %d: Failed to read " - "deny_mac_file '%s'\n", - line, pos); - errors++; - } - } else if (strcmp(buf, "ap_max_inactivity") == 0) { - bss->ap_max_inactivity = atoi(pos); - } else if (strcmp(buf, "country_code") == 0) { - memcpy(conf->country, pos, 2); - /* FIX: make this configurable */ - conf->country[2] = ' '; - } else if (strcmp(buf, "ieee80211d") == 0) { - conf->ieee80211d = atoi(pos); - } else if (strcmp(buf, "ieee80211h") == 0) { - conf->ieee80211h = atoi(pos); - } else if (strcmp(buf, "assoc_ap_addr") == 0) { - if (hwaddr_aton(pos, bss->assoc_ap_addr)) { - printf("Line %d: invalid MAC address '%s'\n", - line, pos); - errors++; - } - bss->assoc_ap = 1; - } else if (strcmp(buf, "ieee8021x") == 0) { - bss->ieee802_1x = atoi(pos); - } else if (strcmp(buf, "eapol_version") == 0) { - bss->eapol_version = atoi(pos); - if (bss->eapol_version < 1 || - bss->eapol_version > 2) { - printf("Line %d: invalid EAPOL " - "version (%d): '%s'.\n", - line, bss->eapol_version, pos); - errors++; - } else - wpa_printf(MSG_DEBUG, "eapol_version=%d", - bss->eapol_version); -#ifdef EAP_SERVER - } else if (strcmp(buf, "eap_authenticator") == 0) { - bss->eap_server = atoi(pos); - printf("Line %d: obsolete eap_authenticator used; " - "this has been renamed to eap_server\n", line); - } else if (strcmp(buf, "eap_server") == 0) { - bss->eap_server = atoi(pos); - } else if (strcmp(buf, "eap_user_file") == 0) { - if (hostapd_config_read_eap_user(pos, bss)) - errors++; - } else if (strcmp(buf, "ca_cert") == 0) { - free(bss->ca_cert); - bss->ca_cert = strdup(pos); - } else if (strcmp(buf, "server_cert") == 0) { - free(bss->server_cert); - bss->server_cert = strdup(pos); - } else if (strcmp(buf, "private_key") == 0) { - free(bss->private_key); - bss->private_key = strdup(pos); - } else if (strcmp(buf, "private_key_passwd") == 0) { - free(bss->private_key_passwd); - bss->private_key_passwd = strdup(pos); - } else if (strcmp(buf, "check_crl") == 0) { - bss->check_crl = atoi(pos); -#ifdef EAP_SIM - } else if (strcmp(buf, "eap_sim_db") == 0) { - free(bss->eap_sim_db); - bss->eap_sim_db = strdup(pos); -#endif /* EAP_SIM */ -#endif /* EAP_SERVER */ - } else if (strcmp(buf, "eap_message") == 0) { - char *term; - bss->eap_req_id_text = strdup(pos); - if (bss->eap_req_id_text == NULL) { - printf("Line %d: Failed to allocate memory " - "for eap_req_id_text\n", line); - errors++; - continue; - } - bss->eap_req_id_text_len = - strlen(bss->eap_req_id_text); - term = strstr(bss->eap_req_id_text, "\\0"); - if (term) { - *term++ = '\0'; - memmove(term, term + 1, - bss->eap_req_id_text_len - - (term - bss->eap_req_id_text) - 1); - bss->eap_req_id_text_len--; - } - } else if (strcmp(buf, "wep_key_len_broadcast") == 0) { - bss->default_wep_key_len = atoi(pos); - if (bss->default_wep_key_len > 13) { - printf("Line %d: invalid WEP key len %lu " - "(= %lu bits)\n", line, - (unsigned long) - bss->default_wep_key_len, - (unsigned long) - bss->default_wep_key_len * 8); - errors++; - } - } else if (strcmp(buf, "wep_key_len_unicast") == 0) { - bss->individual_wep_key_len = atoi(pos); - if (bss->individual_wep_key_len < 0 || - bss->individual_wep_key_len > 13) { - printf("Line %d: invalid WEP key len %d " - "(= %d bits)\n", line, - bss->individual_wep_key_len, - bss->individual_wep_key_len * 8); - errors++; - } - } else if (strcmp(buf, "wep_rekey_period") == 0) { - bss->wep_rekeying_period = atoi(pos); - if (bss->wep_rekeying_period < 0) { - printf("Line %d: invalid period %d\n", - line, bss->wep_rekeying_period); - errors++; - } - } else if (strcmp(buf, "eap_reauth_period") == 0) { - bss->eap_reauth_period = atoi(pos); - if (bss->eap_reauth_period < 0) { - printf("Line %d: invalid period %d\n", - line, bss->eap_reauth_period); - errors++; - } - } else if (strcmp(buf, "eapol_key_index_workaround") == 0) { - bss->eapol_key_index_workaround = atoi(pos); -#ifdef CONFIG_IAPP - } else if (strcmp(buf, "iapp_interface") == 0) { - bss->ieee802_11f = 1; - snprintf(bss->iapp_iface, sizeof(bss->iapp_iface), - "%s", pos); -#endif /* CONFIG_IAPP */ - } else if (strcmp(buf, "own_ip_addr") == 0) { - if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (strcmp(buf, "nas_identifier") == 0) { - bss->nas_identifier = strdup(pos); - } else if (strcmp(buf, "auth_server_addr") == 0) { - if (hostapd_config_read_radius_addr( - &bss->radius->auth_servers, - &bss->radius->num_auth_servers, pos, 1812, - &bss->radius->auth_server)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (bss->radius->auth_server && - strcmp(buf, "auth_server_port") == 0) { - bss->radius->auth_server->port = atoi(pos); - } else if (bss->radius->auth_server && - strcmp(buf, "auth_server_shared_secret") == 0) { - int len = strlen(pos); - if (len == 0) { - /* RFC 2865, Ch. 3 */ - printf("Line %d: empty shared secret is not " - "allowed.\n", line); - errors++; - } - bss->radius->auth_server->shared_secret = - (u8 *) strdup(pos); - bss->radius->auth_server->shared_secret_len = len; - } else if (strcmp(buf, "acct_server_addr") == 0) { - if (hostapd_config_read_radius_addr( - &bss->radius->acct_servers, - &bss->radius->num_acct_servers, pos, 1813, - &bss->radius->acct_server)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (bss->radius->acct_server && - strcmp(buf, "acct_server_port") == 0) { - bss->radius->acct_server->port = atoi(pos); - } else if (bss->radius->acct_server && - strcmp(buf, "acct_server_shared_secret") == 0) { - int len = strlen(pos); - if (len == 0) { - /* RFC 2865, Ch. 3 */ - printf("Line %d: empty shared secret is not " - "allowed.\n", line); - errors++; - } - bss->radius->acct_server->shared_secret = - (u8 *) strdup(pos); - bss->radius->acct_server->shared_secret_len = len; - } else if (strcmp(buf, "radius_retry_primary_interval") == 0) { - bss->radius->retry_primary_interval = atoi(pos); - } else if (strcmp(buf, "radius_acct_interim_interval") == 0) { - bss->radius->acct_interim_interval = atoi(pos); - } else if (strcmp(buf, "auth_algs") == 0) { - bss->auth_algs = atoi(pos); - if (bss->auth_algs == 0) { - printf("Line %d: no authentication algorithms " - "allowed\n", - line); - errors++; - } - } else if (strcmp(buf, "max_num_sta") == 0) { - bss->max_num_sta = atoi(pos); - if (bss->max_num_sta < 0 || - bss->max_num_sta > MAX_STA_COUNT) { - printf("Line %d: Invalid max_num_sta=%d; " - "allowed range 0..%d\n", line, - bss->max_num_sta, MAX_STA_COUNT); - errors++; - } - } else if (strcmp(buf, "wpa") == 0) { - bss->wpa = atoi(pos); - } else if (strcmp(buf, "wpa_group_rekey") == 0) { - bss->wpa_group_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_strict_rekey") == 0) { - bss->wpa_strict_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_gmk_rekey") == 0) { - bss->wpa_gmk_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_passphrase") == 0) { - int len = strlen(pos); - if (len < 8 || len > 63) { - printf("Line %d: invalid WPA passphrase length" - " %d (expected 8..63)\n", line, len); - errors++; - } else { - free(bss->ssid.wpa_passphrase); - bss->ssid.wpa_passphrase = strdup(pos); - } - } else if (strcmp(buf, "wpa_psk") == 0) { - free(bss->ssid.wpa_psk); - bss->ssid.wpa_psk = - wpa_zalloc(sizeof(struct hostapd_wpa_psk)); - if (bss->ssid.wpa_psk == NULL) - errors++; - else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, - PMK_LEN) || - pos[PMK_LEN * 2] != '\0') { - printf("Line %d: Invalid PSK '%s'.\n", line, - pos); - errors++; - } else { - bss->ssid.wpa_psk->group = 1; - } - } else if (strcmp(buf, "wpa_psk_file") == 0) { - free(bss->ssid.wpa_psk_file); - bss->ssid.wpa_psk_file = strdup(pos); - if (!bss->ssid.wpa_psk_file) { - printf("Line %d: allocation failed\n", line); - errors++; - } - } else if (strcmp(buf, "wpa_key_mgmt") == 0) { - bss->wpa_key_mgmt = - hostapd_config_parse_key_mgmt(line, pos); - if (bss->wpa_key_mgmt == -1) - errors++; - } else if (strcmp(buf, "wpa_pairwise") == 0) { - bss->wpa_pairwise = - hostapd_config_parse_cipher(line, pos); - if (bss->wpa_pairwise == -1 || - bss->wpa_pairwise == 0) - errors++; - else if (bss->wpa_pairwise & - (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | - WPA_CIPHER_WEP104)) { - printf("Line %d: unsupported pairwise " - "cipher suite '%s'\n", - bss->wpa_pairwise, pos); - errors++; - } else { - if (bss->wpa_pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else - bss->wpa_group = WPA_CIPHER_CCMP; - } -#ifdef CONFIG_RSN_PREAUTH - } else if (strcmp(buf, "rsn_preauth") == 0) { - bss->rsn_preauth = atoi(pos); - } else if (strcmp(buf, "rsn_preauth_interfaces") == 0) { - bss->rsn_preauth_interfaces = strdup(pos); -#endif /* CONFIG_RSN_PREAUTH */ -#ifdef CONFIG_PEERKEY - } else if (strcmp(buf, "peerkey") == 0) { - bss->peerkey = atoi(pos); -#endif /* CONFIG_PEERKEY */ - } else if (strcmp(buf, "ctrl_interface") == 0) { - free(bss->ctrl_interface); - bss->ctrl_interface = strdup(pos); - } else if (strcmp(buf, "ctrl_interface_group") == 0) { -#ifndef CONFIG_NATIVE_WINDOWS - struct group *grp; - char *endp; - const char *group = pos; - - grp = getgrnam(group); - if (grp) { - bss->ctrl_interface_gid = grp->gr_gid; - bss->ctrl_interface_gid_set = 1; - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" - " (from group name '%s')", - bss->ctrl_interface_gid, group); - continue; - } - - /* Group name not found - try to parse this as gid */ - bss->ctrl_interface_gid = strtol(group, &endp, 10); - if (*group == '\0' || *endp != '\0') { - wpa_printf(MSG_DEBUG, "Line %d: Invalid group " - "'%s'", line, group); - errors++; - continue; - } - bss->ctrl_interface_gid_set = 1; - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", - bss->ctrl_interface_gid); -#endif /* CONFIG_NATIVE_WINDOWS */ -#ifdef RADIUS_SERVER - } else if (strcmp(buf, "radius_server_clients") == 0) { - free(bss->radius_server_clients); - bss->radius_server_clients = strdup(pos); - } else if (strcmp(buf, "radius_server_auth_port") == 0) { - bss->radius_server_auth_port = atoi(pos); - } else if (strcmp(buf, "radius_server_ipv6") == 0) { - bss->radius_server_ipv6 = atoi(pos); -#endif /* RADIUS_SERVER */ - } else if (strcmp(buf, "test_socket") == 0) { - free(bss->test_socket); - bss->test_socket = strdup(pos); - } else if (strcmp(buf, "use_pae_group_addr") == 0) { - bss->use_pae_group_addr = atoi(pos); - } else if (strcmp(buf, "hw_mode") == 0) { - if (strcmp(pos, "a") == 0) - conf->hw_mode = HOSTAPD_MODE_IEEE80211A; - else if (strcmp(pos, "b") == 0) - conf->hw_mode = HOSTAPD_MODE_IEEE80211B; - else if (strcmp(pos, "g") == 0) - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - else { - printf("Line %d: unknown hw_mode '%s'\n", - line, pos); - errors++; - } - } else if (strcmp(buf, "channel") == 0) { - conf->channel = atoi(pos); - } else if (strcmp(buf, "beacon_int") == 0) { - int val = atoi(pos); - /* MIB defines range as 1..65535, but very small values - * cause problems with the current implementation. - * Since it is unlikely that this small numbers are - * useful in real life scenarios, do not allow beacon - * period to be set below 15 TU. */ - if (val < 15 || val > 65535) { - printf("Line %d: invalid beacon_int %d " - "(expected 15..65535)\n", - line, val); - errors++; - } else - conf->beacon_int = val; - } else if (strcmp(buf, "dtim_period") == 0) { - bss->dtim_period = atoi(pos); - if (bss->dtim_period < 1 || bss->dtim_period > 255) { - printf("Line %d: invalid dtim_period %d\n", - line, bss->dtim_period); - errors++; - } - } else if (strcmp(buf, "rts_threshold") == 0) { - conf->rts_threshold = atoi(pos); - if (conf->rts_threshold < 0 || - conf->rts_threshold > 2347) { - printf("Line %d: invalid rts_threshold %d\n", - line, conf->rts_threshold); - errors++; - } - } else if (strcmp(buf, "fragm_threshold") == 0) { - conf->fragm_threshold = atoi(pos); - if (conf->fragm_threshold < 256 || - conf->fragm_threshold > 2346) { - printf("Line %d: invalid fragm_threshold %d\n", - line, conf->fragm_threshold); - errors++; - } - } else if (strcmp(buf, "send_probe_response") == 0) { - int val = atoi(pos); - if (val != 0 && val != 1) { - printf("Line %d: invalid send_probe_response " - "%d (expected 0 or 1)\n", line, val); - } else - conf->send_probe_response = val; - } else if (strcmp(buf, "supported_rates") == 0) { - if (hostapd_parse_rates(&conf->supported_rates, pos)) { - printf("Line %d: invalid rate list\n", line); - errors++; - } - } else if (strcmp(buf, "basic_rates") == 0) { - if (hostapd_parse_rates(&conf->basic_rates, pos)) { - printf("Line %d: invalid rate list\n", line); - errors++; - } - } else if (strcmp(buf, "ignore_broadcast_ssid") == 0) { - bss->ignore_broadcast_ssid = atoi(pos); - } else if (strcmp(buf, "bridge_packets") == 0) { - conf->bridge_packets = atoi(pos); - } else if (strcmp(buf, "wep_default_key") == 0) { - bss->ssid.wep.idx = atoi(pos); - if (bss->ssid.wep.idx > 3) { - printf("Invalid wep_default_key index %d\n", - bss->ssid.wep.idx); - errors++; - } - } else if (strcmp(buf, "wep_key0") == 0 || - strcmp(buf, "wep_key1") == 0 || - strcmp(buf, "wep_key2") == 0 || - strcmp(buf, "wep_key3") == 0) { - if (hostapd_config_read_wep(&bss->ssid.wep, - buf[7] - '0', pos)) { - printf("Line %d: invalid WEP key '%s'\n", - line, buf); - errors++; - } - } else if (strcmp(buf, "dynamic_vlan") == 0) { - bss->ssid.dynamic_vlan = atoi(pos); - } else if (strcmp(buf, "vlan_file") == 0) { - if (hostapd_config_read_vlan_file(bss, pos)) { - printf("Line %d: failed to read VLAN file " - "'%s'\n", line, pos); - errors++; - } -#ifdef CONFIG_FULL_DYNAMIC_VLAN - } else if (strcmp(buf, "vlan_tagged_interface") == 0) { - bss->ssid.vlan_tagged_interface = strdup(pos); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - } else if (strcmp(buf, "passive_scan_interval") == 0) { - conf->passive_scan_interval = atoi(pos); - } else if (strcmp(buf, "passive_scan_listen") == 0) { - conf->passive_scan_listen = atoi(pos); - } else if (strcmp(buf, "passive_scan_mode") == 0) { - conf->passive_scan_mode = atoi(pos); - } else if (strcmp(buf, "ap_table_max_size") == 0) { - conf->ap_table_max_size = atoi(pos); - } else if (strcmp(buf, "ap_table_expiration_time") == 0) { - conf->ap_table_expiration_time = atoi(pos); - } else if (strncmp(buf, "tx_queue_", 9) == 0) { - if (hostapd_config_tx_queue(conf, buf, pos)) { - printf("Line %d: invalid TX queue item\n", - line); - errors++; - } - } else if (strcmp(buf, "wme_enabled") == 0) { - bss->wme_enabled = atoi(pos); - } else if (strncmp(buf, "wme_ac_", 7) == 0) { - if (hostapd_config_wme_ac(conf, buf, pos)) { - printf("Line %d: invalid wme ac item\n", - line); - errors++; - } - } else if (strcmp(buf, "bss") == 0) { - if (hostapd_config_bss(conf, pos)) { - printf("Line %d: invalid bss item\n", line); - errors++; - } - } else if (strcmp(buf, "bssid") == 0) { - if (bss == conf->bss) { - printf("Line %d: bssid item not allowed " - "for the default interface\n", line); - errors++; - } else if (hwaddr_aton(pos, bss->bssid)) { - printf("Line %d: invalid bssid item\n", line); - errors++; - } -#ifdef CONFIG_IEEE80211W - } else if (strcmp(buf, "ieee80211w") == 0) { - bss->ieee80211w = atoi(pos); -#endif /* CONFIG_IEEE80211W */ - } else { - printf("Line %d: unknown configuration item '%s'\n", - line, buf); - errors++; - } - } - - fclose(f); - - if (bss->individual_wep_key_len == 0) { - /* individual keys are not use; can use key idx0 for broadcast - * keys */ - bss->broadcast_key_idx_min = 0; - } - - for (i = 0; i < conf->num_bss; i++) { - bss = &conf->bss[i]; - - bss->radius->auth_server = bss->radius->auth_servers; - bss->radius->acct_server = bss->radius->acct_servers; - - if (bss->wpa && bss->ieee802_1x) { - bss->ssid.security_policy = SECURITY_WPA; - } else if (bss->wpa) { - bss->ssid.security_policy = SECURITY_WPA_PSK; - } else if (bss->ieee802_1x) { - bss->ssid.security_policy = SECURITY_IEEE_802_1X; - bss->ssid.wep.default_len = bss->default_wep_key_len; - } else if (bss->ssid.wep.keys_set) - bss->ssid.security_policy = SECURITY_STATIC_WEP; - else - bss->ssid.security_policy = SECURITY_PLAINTEXT; - } - - if (hostapd_config_check(conf)) - errors++; - - if (errors) { - printf("%d errors found in configuration file '%s'\n", - errors, fname); - hostapd_config_free(conf); - conf = NULL; - } - - return conf; -} - - -int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) -{ - int i; - - if (a->idx != b->idx || a->default_len != b->default_len) - return 1; - for (i = 0; i < NUM_WEP_KEYS; i++) - if (a->len[i] != b->len[i] || - memcmp(a->key[i], b->key[i], a->len[i]) != 0) - return 1; - return 0; -} - - -static void hostapd_config_free_radius(struct hostapd_radius_server *servers, - int num_servers) -{ - int i; - - for (i = 0; i < num_servers; i++) { - free(servers[i].shared_secret); - } - free(servers); -} - - -static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) -{ - free(user->identity); - free(user->password); - free(user); -} - - -static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) -{ - int i; - for (i = 0; i < NUM_WEP_KEYS; i++) { - free(keys->key[i]); - keys->key[i] = NULL; - } -} - - -static void hostapd_config_free_bss(struct hostapd_bss_config *conf) -{ - struct hostapd_wpa_psk *psk, *prev; - struct hostapd_eap_user *user, *prev_user; - - if (conf == NULL) - return; - - psk = conf->ssid.wpa_psk; - while (psk) { - prev = psk; - psk = psk->next; - free(prev); - } - - free(conf->ssid.wpa_passphrase); - free(conf->ssid.wpa_psk_file); -#ifdef CONFIG_FULL_DYNAMIC_VLAN - free(conf->ssid.vlan_tagged_interface); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } - - free(conf->dump_log_name); - free(conf->eap_req_id_text); - free(conf->accept_mac); - free(conf->deny_mac); - free(conf->nas_identifier); - hostapd_config_free_radius(conf->radius->auth_servers, - conf->radius->num_auth_servers); - hostapd_config_free_radius(conf->radius->acct_servers, - conf->radius->num_acct_servers); - free(conf->rsn_preauth_interfaces); - free(conf->ctrl_interface); - free(conf->ca_cert); - free(conf->server_cert); - free(conf->private_key); - free(conf->private_key_passwd); - free(conf->eap_sim_db); - free(conf->radius_server_clients); - free(conf->test_socket); - free(conf->radius); - hostapd_config_free_vlan(conf); - if (conf->ssid.dyn_vlan_keys) { - struct hostapd_ssid *ssid = &conf->ssid; - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - if (ssid->dyn_vlan_keys[i] == NULL) - continue; - hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); - free(ssid->dyn_vlan_keys[i]); - } - free(ssid->dyn_vlan_keys); - ssid->dyn_vlan_keys = NULL; - } -} - - -void hostapd_config_free(struct hostapd_config *conf) -{ - size_t i; - - if (conf == NULL) - return; - - for (i = 0; i < conf->num_bss; i++) - hostapd_config_free_bss(&conf->bss[i]); - free(conf->bss); - - free(conf); -} - - -/* Perform a binary search for given MAC address from a pre-sorted list. - * Returns 1 if address is in the list or 0 if not. */ -int hostapd_maclist_found(macaddr *list, int num_entries, const u8 *addr) -{ - int start, end, middle, res; - - start = 0; - end = num_entries - 1; - - while (start <= end) { - middle = (start + end) / 2; - res = memcmp(list[middle], addr, ETH_ALEN); - if (res == 0) - return 1; - if (res < 0) - start = middle + 1; - else - end = middle - 1; - } - - return 0; -} - - -int hostapd_rate_found(int *list, int rate) -{ - int i; - - if (list == NULL) - return 0; - - for (i = 0; list[i] >= 0; i++) - if (list[i] == rate) - return 1; - - return 0; -} - - -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) -{ - struct hostapd_vlan *v = vlan; - while (v) { - if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) - return v->ifname; - v = v->next; - } - return NULL; -} - - -const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, - const u8 *addr, const u8 *prev_psk) -{ - struct hostapd_wpa_psk *psk; - int next_ok = prev_psk == NULL; - - for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { - if (next_ok && - (psk->group || memcmp(psk->addr, addr, ETH_ALEN) == 0)) - return psk->psk; - - if (psk->psk == prev_psk) - next_ok = 1; - } - - return NULL; -} - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - struct hostapd_eap_user *user = conf->eap_user; - - while (user) { - if (!phase2 && user->identity == NULL) { - /* Wildcard match */ - break; - } - - if (!phase2 && user->wildcard_prefix && - identity_len >= user->identity_len && - memcmp(user->identity, identity, user->identity_len) == 0) - { - /* Wildcard prefix match */ - break; - } - - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; - } - - return user; -} diff --git a/contrib/hostapd/developer.txt b/contrib/hostapd/developer.txt deleted file mode 100644 index e1d3163413902..0000000000000 --- a/contrib/hostapd/developer.txt +++ /dev/null @@ -1,219 +0,0 @@ -Developer notes for hostapd -=========================== - -hostapd daemon setup, operations, and shutdown ----------------------------------------------- - -Files: hostapd.[ch] - -Externally called functions: - hostapd_new_assoc_sta() is called when a station associates with the AP - -Event loop functions: - handle_term() is called on SIGINT and SIGTERM to terminate hostapd process - handle_reload() is called on SIGHUP to reload configuration - handle_dump_state() is called on SIGUSR1 to dump station state data to a - text file - hostapd_rotate_wep() is called to periodically change WEP keys - - -Configuration parsing ---------------------- - -Configuration file parsing and data structure definition. - -Files: config.[ch] - -Externally called functions: - hostapd_config_read() is called to read and parse a configuration file; - allocates and returns configuration data structure - hostapd_config_free() is called to free configuration data structure - hostapd_maclist_found() is called to check whether a given address is found - in a list of MAC addresses - - -Kernel driver access --------------------- - -Helper functions for configuring the Host AP kernel driver and -accessing data from it. - -Files: driver.[ch] - - -IEEE 802.11 frame handling (netdevice wlan#ap) ----------------------------------------------- - -Receive all incoming IEEE 802.11 frames from the kernel driver via -wlan#ap interface. - -Files: receive.c - -Externally called functions: - hostapd_init_sockets() is called to initialize sockets for receiving and - sending IEEE 802.11 frames via wlan#ap interface - -Event loop functions: - handle_read() is called for each incoming packet from wlan#ap net device - - -Station table -------------- - -Files: sta_info.[ch], ap.h - -Event loop functions: - ap_handle_timer() is called to check station activity and to remove - inactive stations - - -IEEE 802.11 management ----------------------- - -IEEE 802.11 management frame sending and processing (mainly, -authentication and association). IEEE 802.11 station functionality -(authenticate and associate with another AP as an station). - -Files: ieee802_11.[ch] - -Externally called functions: - ieee802_11_mgmt() is called for each received IEEE 802.11 management frame - (from handle_frame() in hostapd.c) - ieee802_11_mgmt_cb() is called for each received TX callback of IEEE 802.11 - management frame (from handle_tx_callback() in hostapd.c) - ieee802_11_send_deauth() is called to send deauthentication frame - ieee802_11_send_disassoc() is called to send disassociation frame - ieee802_11_parse_elems() is used to parse information elements in - IEEE 802.11 management frames - -Event loop functions: - ieee802_11_sta_authenticate() called to retry authentication (with another - AP) - ieee802_11_sta_associate() called to retry association (with another AP) - - -IEEE 802.11 authentication --------------------------- - -Access control list for IEEE 802.11 authentication. Uses staticly -configured ACL from configuration files or an external RADIUS -server. Results from external RADIUS queries are cached to allow -faster authentication frame processing. - -Files: ieee802_11_auth.[ch] - -Externally called functions: - hostapd_acl_init() called once during hostapd startup - hostapd_acl_deinit() called once during hostapd shutdown - hostapd_acl_recv_radius() called by IEEE 802.1X code for incoming RADIUS - Authentication messages (returns 0 if message was processed) - hostapd_allowed_address() called to check whether a specified station can be - authenticated - -Event loop functions: - hostapd_acl_expire() is called to expire ACL cache entries - - -IEEE 802.1X Authenticator -------------------------- - -Files: ieee802_1x.[ch] - - -Externally called functions: - ieee802_1x_receive() is called for each incoming EAPOL frame from the - wireless interface - ieee802_1x_new_station() is called to start IEEE 802.1X authentication when - a new station completes IEEE 802.11 association - -Event loop functions: - ieee802_1x_receive_auth() called for each incoming RADIUS Authentication - message - - -EAPOL state machine -------------------- - -IEEE 802.1X state machine for EAPOL. - -Files: eapol_sm.[ch] - -Externally called functions: - eapol_sm_step() is called to advance EAPOL state machines after any change - that could affect their state - -Event loop functions: - eapol_port_timers_tick() called once per second to advance Port Timers state - machine - - -IEEE 802.11f (IAPP) -------------------- - -Files: iapp.[ch] - -Externally called functions: - iapp_new_station() is called to start accounting session when a new station - completes IEEE 802.11 association or IEEE 802.1X authentication - -Event loop functions: - iapp_receive_udp() is called for incoming IAPP frames over UDP - - -Per station accounting ----------------------- - -Send RADIUS Accounting start and stop messages to a RADIUS Accounting -server. Process incoming RADIUS Accounting messages. - -Files: accounting.[ch] - -Externally called functions: - accounting_init() called once during hostapd startup - accounting_deinit() called once during hostapd shutdown - accounting_sta_start() called when a station starts new session - accounting_sta_stop() called when a station session is terminated - -Event loop functions: - accounting_receive() called for each incoming RADIUS Accounting message - accounting_list_timer() called to retransmit accounting messages and to - remove expired entries - - -RADIUS messages ---------------- - -RADIUS message generation and parsing functions. - -Files: radius.[ch] - - -Event loop ----------- - -Event loop for registering timeout calls, signal handlers, and socket -read events. - -Files: eloop.[ch] - - -RC4 ---- - -RC4 encryption - -Files: rc4.[ch] - - -MD5 ---- - -MD5 hash and HMAC-MD5. - -Files: md5.[ch] - - -Miscellaneous helper functions ------------------------------- - -Files: common.[ch] diff --git a/contrib/hostapd/eap.h b/contrib/hostapd/eap.h deleted file mode 100644 index 137719189dfa8..0000000000000 --- a/contrib/hostapd/eap.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * hostapd / EAP Standalone Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_H -#define EAP_H - -#include "defs.h" -#include "eap_defs.h" -#include "eap_methods.h" - -struct eap_sm; - -#define EAP_MAX_METHODS 8 -struct eap_user { - struct { - int vendor; - u32 method; - } methods[EAP_MAX_METHODS]; - u8 *password; - size_t password_len; - int password_hash; /* whether password is hashed with - * nt_password_hash() */ - int phase2; - int force_version; -}; - -enum eapol_bool_var { - EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, - EAPOL_eapReq, EAPOL_eapNoReq, EAPOL_portEnabled, EAPOL_eapTimeout -}; - -struct eapol_callbacks { - Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); - void (*set_bool)(void *ctx, enum eapol_bool_var variable, - Boolean value); - void (*set_eapReqData)(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen); - void (*set_eapKeyData)(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen); - int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, - int phase2, struct eap_user *user); - const char * (*get_eap_req_id_text)(void *ctx, size_t *len); -}; - -struct eap_config { - void *ssl_ctx; - void *eap_sim_db_priv; - Boolean backend_auth; -}; - - -#ifdef EAP_SERVER - -struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf); -void eap_sm_deinit(struct eap_sm *sm); -int eap_sm_step(struct eap_sm *sm); -void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, - size_t eapRespDataLen); -void eap_sm_notify_cached(struct eap_sm *sm); -void eap_sm_pending_cb(struct eap_sm *sm); -int eap_sm_method_pending(struct eap_sm *sm); - -#else /* EAP_SERVER */ - -static inline struct eap_sm * eap_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf) -{ - return NULL; -} - -static inline void eap_sm_deinit(struct eap_sm *sm) -{ -} - -static inline int eap_sm_step(struct eap_sm *sm) -{ - return 0; -} - - -static inline void eap_set_eapRespData(struct eap_sm *sm, - const u8 *eapRespData, - size_t eapRespDataLen) -{ -} - -static inline void eap_sm_notify_cached(struct eap_sm *sm) -{ -} - -static inline void eap_sm_pending_cb(struct eap_sm *sm) -{ -} - -static inline int eap_sm_method_pending(struct eap_sm *sm) -{ - return 0; -} - -#endif /* EAP_SERVER */ - -#endif /* EAP_H */ diff --git a/contrib/hostapd/eap_peap.c b/contrib/hostapd/eap_peap.c deleted file mode 100644 index 6ea1f618a1d8f..0000000000000 --- a/contrib/hostapd/eap_peap.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "tls.h" - - -/* Maximum supported PEAP version - * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt - * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt - * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt - */ -#define EAP_PEAP_VERSION 1 - - -static void eap_peap_reset(struct eap_sm *sm, void *priv); - - -struct eap_peap_data { - struct eap_ssl_data ssl; - enum { - START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, - PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE - } state; - - int peap_version; - const struct eap_method *phase2_method; - void *phase2_priv; - int force_version; -}; - - -static const char * eap_peap_state_txt(int state) -{ - switch (state) { - case START: - return "START"; - case PHASE1: - return "PHASE1"; - case PHASE2_START: - return "PHASE2_START"; - case PHASE2_ID: - return "PHASE2_ID"; - case PHASE2_METHOD: - return "PHASE2_METHOD"; - case PHASE2_TLV: - return "PHASE2_TLV"; - case SUCCESS_REQ: - return "SUCCESS_REQ"; - case FAILURE_REQ: - return "FAILURE_REQ"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - default: - return "Unknown?!"; - } -} - - -static void eap_peap_state(struct eap_peap_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", - eap_peap_state_txt(data->state), - eap_peap_state_txt(state)); - data->state = state; -} - - -static EapType eap_peap_req_success(struct eap_sm *sm, - struct eap_peap_data *data) -{ - if (data->state == FAILURE || data->state == FAILURE_REQ) { - eap_peap_state(data, FAILURE); - return EAP_TYPE_NONE; - } - - if (data->peap_version == 0) { - sm->tlv_request = TLV_REQ_SUCCESS; - eap_peap_state(data, PHASE2_TLV); - return EAP_TYPE_TLV; - } else { - eap_peap_state(data, SUCCESS_REQ); - return EAP_TYPE_NONE; - } -} - - -static EapType eap_peap_req_failure(struct eap_sm *sm, - struct eap_peap_data *data) -{ - if (data->state == FAILURE || data->state == FAILURE_REQ || - data->state == SUCCESS_REQ || - (data->phase2_method && - data->phase2_method->method == EAP_TYPE_TLV)) { - eap_peap_state(data, FAILURE); - return EAP_TYPE_NONE; - } - - if (data->peap_version == 0) { - sm->tlv_request = TLV_REQ_FAILURE; - eap_peap_state(data, PHASE2_TLV); - return EAP_TYPE_TLV; - } else { - eap_peap_state(data, FAILURE_REQ); - return EAP_TYPE_NONE; - } -} - - -static void * eap_peap_init(struct eap_sm *sm) -{ - struct eap_peap_data *data; - - data = wpa_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->peap_version = EAP_PEAP_VERSION; - data->force_version = -1; - if (sm->user && sm->user->force_version >= 0) { - data->force_version = sm->user->force_version; - wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", - data->force_version); - data->peap_version = data->force_version; - } - data->state = START; - - if (eap_tls_ssl_init(sm, &data->ssl, 0)) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); - eap_peap_reset(sm, data); - return NULL; - } - - return data; -} - - -static void eap_peap_reset(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - if (data == NULL) - return; - if (data->phase2_priv && data->phase2_method) - data->phase2_method->reset(sm, data->phase2_priv); - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); -} - - -static u8 * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" - " request"); - eap_peap_state(data, FAILURE); - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_PEAP; - *pos = EAP_TLS_FLAGS_START | data->peap_version; - - eap_peap_state(data, PHASE1); - - return (u8 *) req; -} - - -static u8 * eap_peap_build_req(struct eap_sm *sm, struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, &req, - reqDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting " - "Phase2"); - eap_peap_state(data, PHASE2_START); - } - - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_PEAP, - data->peap_version); - return req; -} - - -static u8 * eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, - int id, u8 *plain, size_t plain_len, - size_t *out_len) -{ - int res; - u8 *pos; - struct eap_hdr *req; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_PEAP; - *pos++ = data->peap_version; - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, - pos, data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " - "data"); - free(req); - return NULL; - } - - *out_len = sizeof(struct eap_hdr) + 2 + res; - req->length = host_to_be16(*out_len); - return (u8 *) req; -} - - -static u8 * eap_peap_build_phase2_req(struct eap_sm *sm, - struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - u8 *req, *buf, *encr_req; - size_t req_len; - - buf = req = data->phase2_method->buildReq(sm, data->phase2_priv, id, - &req_len); - if (req == NULL) - return NULL; - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", - req, req_len); - - if (data->peap_version == 0 && - data->phase2_method->method != EAP_TYPE_TLV) { - req += sizeof(struct eap_hdr); - req_len -= sizeof(struct eap_hdr); - } - - encr_req = eap_peap_encrypt(sm, data, id, req, req_len, reqDataLen); - free(buf); - - return encr_req; -} - - -static u8 * eap_peap_build_phase2_term(struct eap_sm *sm, - struct eap_peap_data *data, - int id, size_t *reqDataLen, int success) -{ - u8 *encr_req; - size_t req_len; - struct eap_hdr *hdr; - - req_len = sizeof(*hdr); - hdr = wpa_zalloc(req_len); - if (hdr == NULL) - return NULL; - - hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; - hdr->identifier = id; - hdr->length = htons(req_len); - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", - (u8 *) hdr, req_len); - - encr_req = eap_peap_encrypt(sm, data, id, (u8 *) hdr, req_len, - reqDataLen); - free(hdr); - - return encr_req; -} - - -static u8 * eap_peap_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_peap_data *data = priv; - - switch (data->state) { - case START: - return eap_peap_build_start(sm, data, id, reqDataLen); - case PHASE1: - return eap_peap_build_req(sm, data, id, reqDataLen); - case PHASE2_ID: - case PHASE2_METHOD: - case PHASE2_TLV: - return eap_peap_build_phase2_req(sm, data, id, reqDataLen); - case SUCCESS_REQ: - return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 1); - case FAILURE_REQ: - return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 0); - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", - __func__, data->state); - return NULL; - } -} - - -static Boolean eap_peap_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_PEAP || - (ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - EapType eap_type) -{ - if (data->phase2_priv && data->phase2_method) { - data->phase2_method->reset(sm, data->phase2_priv); - data->phase2_method = NULL; - data->phase2_priv = NULL; - } - data->phase2_method = eap_sm_get_eap_methods(EAP_VENDOR_IETF, - eap_type); - if (!data->phase2_method) - return -1; - - sm->init_phase2 = 1; - data->phase2_priv = data->phase2_method->init(sm); - sm->init_phase2 = 0; - return 0; -} - - -static void eap_peap_process_phase2_response(struct eap_sm *sm, - struct eap_peap_data *data, - u8 *in_data, size_t in_len) -{ - u8 next_type = EAP_TYPE_NONE; - struct eap_hdr *hdr; - u8 *pos; - size_t left; - - if (data->phase2_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " - "initialized?!", __func__); - return; - } - - hdr = (struct eap_hdr *) in_data; - pos = (u8 *) (hdr + 1); - - if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { - left = in_len - sizeof(*hdr); - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " - "allowed types", pos + 1, left - 1); - eap_sm_process_nak(sm, pos + 1, left - 1); - if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index].method != - EAP_TYPE_NONE) { - next_type = sm->user->methods[ - sm->user_eap_method_index++].method; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", - next_type); - } else { - next_type = eap_peap_req_failure(sm, data); - } - eap_peap_phase2_init(sm, data, next_type); - return; - } - - if (data->phase2_method->check(sm, data->phase2_priv, in_data, - in_len)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " - "ignore the packet"); - return; - } - - data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); - - if (!data->phase2_method->isDone(sm, data->phase2_priv)) - return; - - - if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); - next_type = eap_peap_req_failure(sm, data); - eap_peap_phase2_init(sm, data, next_type); - return; - } - - switch (data->state) { - case PHASE2_ID: - if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " - "Identity not found in the user " - "database", - sm->identity, sm->identity_len); - next_type = eap_peap_req_failure(sm, data); - break; - } - - eap_peap_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0].method; - sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); - break; - case PHASE2_METHOD: - next_type = eap_peap_req_success(sm, data); - break; - case PHASE2_TLV: - if (sm->tlv_request == TLV_REQ_SUCCESS || - data->state == SUCCESS_REQ) { - eap_peap_state(data, SUCCESS); - } else { - eap_peap_state(data, FAILURE); - } - break; - case FAILURE: - break; - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", - __func__, data->state); - break; - } - - eap_peap_phase2_init(sm, data, next_type); -} - - -static void eap_peap_process_phase2(struct eap_sm *sm, - struct eap_peap_data *data, - struct eap_hdr *resp, - u8 *in_data, size_t in_len) -{ - u8 *in_decrypted; - int len_decrypted, len, res; - struct eap_hdr *hdr; - size_t buf_len; - - wpa_printf(MSG_DEBUG, "EAP-PEAP: 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; - - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.tls_in_total; - in_decrypted = malloc(buf_len); - if (in_decrypted == NULL) { - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - if (len_decrypted < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " - "data"); - free(in_decrypted); - eap_peap_state(data, FAILURE); - return; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", - in_decrypted, len_decrypted); - - hdr = (struct eap_hdr *) in_decrypted; - - if (data->peap_version == 0 && data->state != PHASE2_TLV) { - struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) + - len_decrypted); - if (nhdr == NULL) { - free(in_decrypted); - return; - } - memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); - free(in_decrypted); - nhdr->code = resp->code; - nhdr->identifier = resp->identifier; - nhdr->length = host_to_be16(sizeof(struct eap_hdr) + - len_decrypted); - - len_decrypted += sizeof(struct eap_hdr); - in_decrypted = (u8 *) nhdr; - } - hdr = (struct eap_hdr *) in_decrypted; - if (len_decrypted < (int) sizeof(*hdr)) { - free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " - "EAP frame (len=%d)", len_decrypted); - eap_peap_req_failure(sm, data); - return; - } - len = be_to_host16(hdr->length); - if (len > len_decrypted) { - free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " - "Phase 2 EAP frame (len=%d hdr->length=%d)", - len_decrypted, len); - eap_peap_req_failure(sm, data); - return; - } - wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " - "identifier=%d length=%d", hdr->code, hdr->identifier, len); - switch (hdr->code) { - case EAP_CODE_RESPONSE: - eap_peap_process_phase2_response(sm, data, (u8 *) hdr, len); - break; - case EAP_CODE_SUCCESS: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); - if (data->state == SUCCESS_REQ) { - eap_peap_state(data, SUCCESS); - } - break; - case EAP_CODE_FAILURE: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); - eap_peap_state(data, FAILURE); - break; - default: - wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " - "Phase 2 EAP header", hdr->code); - break; - } - - free(in_decrypted); - } - - -static void eap_peap_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_peap_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - int peer_version; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; - if (data->force_version >= 0 && peer_version != data->force_version) { - wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" - " version (forced=%d peer=%d) - reject", - data->force_version, peer_version); - eap_peap_state(data, FAILURE); - return; - } - if (peer_version < data->peap_version) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " - "use version %d", - peer_version, data->peap_version, peer_version); - data->peap_version = peer_version; - } - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " - "length"); - eap_peap_state(data, FAILURE); - return; - } - 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; - } - - switch (data->state) { - case PHASE1: - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing " - "failed"); - eap_peap_state(data, FAILURE); - } - break; - case PHASE2_START: - eap_peap_state(data, PHASE2_ID); - eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); - break; - case PHASE2_ID: - case PHASE2_METHOD: - case PHASE2_TLV: - eap_peap_process_phase2(sm, data, resp, pos, left); - break; - case SUCCESS_REQ: - eap_peap_state(data, SUCCESS); - break; - case FAILURE_REQ: - eap_peap_state(data, FAILURE); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", - data->state, __func__); - break; - } - - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-PEAP: Locally detected fatal error " - "in TLS processing"); - eap_peap_state(data, FAILURE); - } -} - - -static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_peap_data *data = priv; - u8 *eapKeyData; - - if (data->state != SUCCESS) - return NULL; - - /* TODO: PEAPv1 - different label in some cases */ - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); - if (eapKeyData) { - *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); - } else { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); - } - - return eapKeyData; -} - - -static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return data->state == SUCCESS; -} - - -int eap_server_peap_register(void) -{ - struct eap_method *eap; - int ret; - - eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); - if (eap == NULL) - return -1; - - eap->init = eap_peap_init; - eap->reset = eap_peap_reset; - eap->buildReq = eap_peap_buildReq; - eap->check = eap_peap_check; - eap->process = eap_peap_process; - eap->isDone = eap_peap_isDone; - eap->getKey = eap_peap_getKey; - eap->isSuccess = eap_peap_isSuccess; - - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; -} diff --git a/contrib/hostapd/eap_tls_common.c b/contrib/hostapd/eap_tls_common.c deleted file mode 100644 index ce61729bc9442..0000000000000 --- a/contrib/hostapd/eap_tls_common.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * hostapd / EAP-TLS/PEAP/TTLS common functions - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "sha1.h" -#include "tls.h" - - -int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer) -{ - data->eap = sm; - data->phase2 = sm->init_phase2; - - data->conn = tls_connection_init(sm->ssl_ctx); - if (data->conn == NULL) { - wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " - "connection"); - return -1; - } - - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { - wpa_printf(MSG_INFO, "SSL: Failed to configure verification " - "of TLS peer certificate"); - tls_connection_deinit(sm->ssl_ctx, data->conn); - data->conn = NULL; - return -1; - } - - /* TODO: make this configurable */ - data->tls_out_limit = 1398; - if (data->phase2) { - /* Limit the fragment size in the inner TLS authentication - * since the outer authentication with EAP-PEAP does not yet - * support fragmentation */ - if (data->tls_out_limit > 100) - data->tls_out_limit -= 100; - } - return 0; -} - - -void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) -{ - tls_connection_deinit(sm->ssl_ctx, data->conn); - free(data->tls_in); - free(data->tls_out); -} - - -u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len) -{ - struct tls_keys keys; - u8 *rnd = NULL, *out; - - out = malloc(len); - if (out == NULL) - return NULL; - - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) - return out; - - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - 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, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; - - free(rnd); - return out; - -fail: - free(out); - free(rnd); - return NULL; -} - - -int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, - u8 **in_data, size_t *in_len) -{ - u8 *buf; - - if (data->tls_in_left > *in_len || data->tls_in) { - if (*in_len == 0) { - wpa_printf(MSG_INFO, "SSL: Empty fragment when trying " - "to reassemble"); - return -1; - } - if (data->tls_in_len + *in_len > 65536) { - /* Limit length to avoid rogue peers from causing large - * memory allocations. */ - free(data->tls_in); - data->tls_in = NULL; - data->tls_in_len = 0; - wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" - " over 64 kB)"); - return -1; - } - 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; - } - 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) { - wpa_printf(MSG_INFO, "SSL: more data than TLS message " - "length indicated"); - data->tls_in_left = 0; - return -1; - } - 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; - } - - *in_data = data->tls_in; - *in_len = data->tls_in_len; - } else - data->tls_in_left = 0; - - return 0; -} - - -int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - u8 *in_data, size_t in_len) -{ - WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); - - if (data->tls_out_len == 0) { - /* No more data to send out - expect to receive more data from - * the peer. */ - int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); - if (res < 0 || res == 1) { - wpa_printf(MSG_DEBUG, "SSL: data reassembly failed"); - return res; - } - /* Full TLS message reassembled - continue handshake processing - */ - if (data->tls_out) { - /* This should not happen.. */ - wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " - "pending tls_out data even though " - "tls_out_len = 0"); - free(data->tls_out); - WPA_ASSERT(data->tls_out == NULL); - } - data->tls_out = tls_connection_server_handshake( - sm->ssl_ctx, data->conn, in_data, in_len, - &data->tls_out_len); - - /* Clear reassembled input data (if the buffer was needed). */ - data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; - free(data->tls_in); - data->tls_in = NULL; - } - - if (data->tls_out == NULL) { - wpa_printf(MSG_DEBUG, "SSL: failed to generate output data"); - data->tls_out_len = 0; - return -1; - } - 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 - * the TLS Message Length field. */ - wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); - free(data->tls_out); - data->tls_out = NULL; - - if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) { - wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal " - "alert - abort handshake"); - return -1; - } - - return 1; - } - - wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " - "%lu bytes)", - (unsigned long) data->tls_out_len - data->tls_out_pos, - (unsigned long) data->tls_out_len); - - return 0; -} - - -int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - u8 **out_data, size_t *out_len) -{ - size_t len; - u8 *pos, *flags; - struct eap_hdr *req; - - *out_len = 0; - - req = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit); - if (req == NULL) { - *out_data = NULL; - return -1; - } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - pos = (u8 *) (req + 1); - *pos++ = eap_type; - flags = pos++; - *flags = peap_version; - if (data->tls_out_pos == 0 && - data->tls_out_len > data->tls_out_limit) { - *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; - *pos++ = (data->tls_out_len >> 24) & 0xff; - *pos++ = (data->tls_out_len >> 16) & 0xff; - *pos++ = (data->tls_out_len >> 8) & 0xff; - *pos++ = data->tls_out_len & 0xff; - } - - len = data->tls_out_len - data->tls_out_pos; - if (len > data->tls_out_limit) { - *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; - len = data->tls_out_limit; - wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " - "will follow", (unsigned long) len); - } - memcpy(pos, &data->tls_out[data->tls_out_pos], len); - data->tls_out_pos += len; - *out_len = (pos - (u8 *) req) + len; - req->length = htons(*out_len); - *out_data = (u8 *) req; - - if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { - data->tls_out_len = 0; - data->tls_out_pos = 0; - free(data->tls_out); - data->tls_out = NULL; - } - - return 0; -} - - -u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, - int peap_version) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(struct eap_hdr) + 2; - req = malloc(*reqDataLen); - if (req == NULL) - return NULL; - wpa_printf(MSG_DEBUG, "SSL: Building ACK"); - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = eap_type; /* Type */ - *pos = peap_version; /* Flags */ - return (u8 *) req; -} diff --git a/contrib/hostapd/eap_tls_common.h b/contrib/hostapd/eap_tls_common.h deleted file mode 100644 index 6c9d69603ed0a..0000000000000 --- a/contrib/hostapd/eap_tls_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * hostapd / EAP-TLS/PEAP/TTLS common functions - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_TLS_COMMON_H -#define EAP_TLS_COMMON_H - -struct eap_ssl_data { - struct tls_connection *conn; - - u8 *tls_out; - size_t tls_out_len; - size_t tls_out_pos; - size_t tls_out_limit; - u8 *tls_in; - size_t tls_in_len; - size_t tls_in_left; - size_t tls_in_total; - - int phase2; - - struct eap_sm *eap; -}; - - -/* EAP TLS Flags */ -#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 -#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 -#define EAP_TLS_FLAGS_START 0x20 -#define EAP_PEAP_VERSION_MASK 0x07 - - /* could be up to 128 bytes, but only the first 64 bytes are used */ -#define EAP_TLS_KEY_LEN 64 - - -int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer); -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); -int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - u8 *in_data, size_t in_len); -int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - u8 **out_data, size_t *out_len); -u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, - int peap_version); - -#endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/hostapd/eap_tlv.c b/contrib/hostapd/eap_tlv.c deleted file mode 100644 index b48e1861c57db..0000000000000 --- a/contrib/hostapd/eap_tlv.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" - - -/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */ -#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ -#define EAP_TLV_NAK_TLV 4 -#define EAP_TLV_CRYPTO_BINDING_TLV 5 -#define EAP_TLV_CONNECTION_BINDING_TLV 6 -#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 -#define EAP_TLV_URI_TLV 8 -#define EAP_TLV_EAP_PAYLOAD_TLV 9 -#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 - -#define EAP_TLV_RESULT_SUCCESS 1 -#define EAP_TLV_RESULT_FAILURE 2 - - -struct eap_tlv_data { - enum { CONTINUE, SUCCESS, FAILURE } state; -}; - - -static void * eap_tlv_init(struct eap_sm *sm) -{ - struct eap_tlv_data *data; - - data = wpa_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->state = CONTINUE; - - return data; -} - - -static void eap_tlv_reset(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - free(data); -} - - -static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - u16 status; - - if (sm->tlv_request == TLV_REQ_SUCCESS) { - status = EAP_TLV_RESULT_SUCCESS; - } else { - status = EAP_TLV_RESULT_FAILURE; - } - - *reqDataLen = sizeof(struct eap_hdr) + 1 + 6; - req = malloc(*reqDataLen); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = host_to_be16(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TLV; - *pos++ = 0x80; /* Mandatory */ - *pos++ = EAP_TLV_RESULT_TLV; - /* Length */ - *pos++ = 0; - *pos++ = 2; - /* Status */ - *pos++ = status >> 8; - *pos++ = status & 0xff; - - return (u8 *) req; -} - - -static Boolean eap_tlv_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV || - (ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_tlv_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_tlv_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - size_t left; - u8 *result_tlv = NULL; - size_t result_tlv_len = 0; - int tlv_type, mandatory, tlv_len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - - /* Parse TLVs */ - left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1; - pos = (u8 *) (resp + 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; - left -= 4; - if ((size_t) tlv_len > left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " - "(tlv_len=%d left=%lu)", tlv_len, - (unsigned long) left); - data->state = FAILURE; - return; - } - switch (tlv_type) { - case EAP_TLV_RESULT_TLV: - result_tlv = pos; - result_tlv_len = tlv_len; - break; - default: - wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " - "%d%s", tlv_type, - mandatory ? " (mandatory)" : ""); - if (mandatory) { - data->state = FAILURE; - return; - } - /* Ignore this TLV, but process other TLVs */ - break; - } - - pos += tlv_len; - left -= tlv_len; - } - if (left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " - "Request (left=%lu)", (unsigned long) left); - data->state = FAILURE; - return; - } - - /* Process supported TLVs */ - if (result_tlv) { - int status; - const char *requested; - - wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", - result_tlv, result_tlv_len); - if (result_tlv_len < 2) { - wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " - "(len=%lu)", - (unsigned long) result_tlv_len); - data->state = FAILURE; - return; - } - requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" : - "Failure"; - status = ((int) result_tlv[0] << 8) | result_tlv[1]; - if (status == EAP_TLV_RESULT_SUCCESS) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " - "- requested %s", requested); - if (sm->tlv_request == TLV_REQ_SUCCESS) - data->state = SUCCESS; - else - data->state = FAILURE; - - } else if (status == EAP_TLV_RESULT_FAILURE) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - " - "requested %s", requested); - if (sm->tlv_request == TLV_REQ_FAILURE) - data->state = SUCCESS; - else - data->state = FAILURE; - } else { - wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " - "Status %d", status); - data->state = FAILURE; - } - } -} - - -static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - return data->state != CONTINUE; -} - - -static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - return data->state == SUCCESS; -} - - -int eap_server_tlv_register(void) -{ - struct eap_method *eap; - int ret; - - eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_TLV, "TLV"); - if (eap == NULL) - return -1; - - eap->init = eap_tlv_init; - eap->reset = eap_tlv_reset; - eap->buildReq = eap_tlv_buildReq; - eap->check = eap_tlv_check; - eap->process = eap_tlv_process; - eap->isDone = eap_tlv_isDone; - eap->isSuccess = eap_tlv_isSuccess; - - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; -} diff --git a/contrib/hostapd/eapol_version.patch b/contrib/hostapd/eapol_version.patch deleted file mode 100644 index c02e3677d0e6d..0000000000000 --- a/contrib/hostapd/eapol_version.patch +++ /dev/null @@ -1,129 +0,0 @@ -? eapol_version.patch -Index: config.c -=================================================================== -RCS file: /home/ncvs/src/contrib/hostapd/config.c,v -retrieving revision 1.1.1.3 -diff -u -r1.1.1.3 config.c ---- config.c 7 Mar 2006 05:46:42 -0000 1.1.1.3 -+++ config.c 27 Mar 2006 05:28:09 -0000 -@@ -29,6 +29,7 @@ - #include "sha1.h" - #include "eap.h" - #include "radius_client.h" -+#include "ieee802_1x.h" /* XXX for EAPOL_VERSION */ - - - static struct hostapd_config *hostapd_config_defaults(void) -@@ -60,6 +61,7 @@ - conf->logger_stdout = (unsigned int) -1; - - conf->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY; -+ conf->eapol_version = EAPOL_VERSION; /* NB: default version */ - - conf->wpa_group_rekey = 600; - conf->wpa_gmk_rekey = 86400; -@@ -855,6 +857,17 @@ - } - } else if (strcmp(buf, "eapol_key_index_workaround") == 0) { - conf->eapol_key_index_workaround = atoi(pos); -+ } else if (strcmp(buf, "eapol_version") == 0) { -+ conf->eapol_version = atoi(pos); -+ if (conf->eapol_version < 1 || -+ conf->eapol_version > 2) { -+ printf("Line %d: invalid EAPOL " -+ "version (%d): '%s'.\n", -+ line, conf->eapol_version, pos); -+ errors++; -+ } else -+ wpa_printf(MSG_DEBUG, "eapol_version=%d", -+ conf->eapol_version); - #ifdef CONFIG_IAPP - } else if (strcmp(buf, "iapp_interface") == 0) { - conf->ieee802_11f = 1; -Index: config.h -=================================================================== -RCS file: /home/ncvs/src/contrib/hostapd/config.h,v -retrieving revision 1.1.1.2 -diff -u -r1.1.1.2 config.h ---- config.h 7 Mar 2006 05:46:42 -0000 1.1.1.2 -+++ config.h 27 Mar 2006 05:28:09 -0000 -@@ -73,6 +73,7 @@ - * EAP Request-Identity */ - size_t eap_req_id_text_len; - int eapol_key_index_workaround; -+ int eapol_version; - - size_t default_wep_key_len; - int individual_wep_key_len; -Index: hostapd.conf -=================================================================== -RCS file: /home/ncvs/src/contrib/hostapd/hostapd.conf,v -retrieving revision 1.1.1.2 -diff -u -r1.1.1.2 hostapd.conf ---- hostapd.conf 7 Mar 2006 05:46:50 -0000 1.1.1.2 -+++ hostapd.conf 27 Mar 2006 05:28:09 -0000 -@@ -106,6 +106,14 @@ - # Require IEEE 802.1X authorization - #ieee8021x=1 - -+# IEEE 802.1X/EAPOL version -+# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL -+# version 2. However, there are some clients that do not handle -+# the new version number correctly (they seem to drop the frames completely). -+# In order to make hostapd interoperate with these clients, the version number -+# can be set to the older version (1) with this configuration value. -+#eapol_version=2 -+ - # Optional displayable message sent with EAP Request-Identity. The first \0 - # in this string will be converted to ASCII-0 (nul). This can be used to - # separate network info (comma separated list of attribute=value pairs); see, -Index: ieee802_1x.c -=================================================================== -RCS file: /home/ncvs/src/contrib/hostapd/ieee802_1x.c,v -retrieving revision 1.4 -diff -u -r1.4 ieee802_1x.c ---- ieee802_1x.c 7 Mar 2006 05:51:52 -0000 1.4 -+++ ieee802_1x.c 27 Mar 2006 05:28:10 -0000 -@@ -76,7 +76,7 @@ - #endif - - xhdr = (struct ieee802_1x_hdr *) buf; -- xhdr->version = EAPOL_VERSION; -+ xhdr->version = hapd->conf->eapol_version; - xhdr->type = type; - xhdr->length = htons(datalen); - -@@ -324,7 +324,7 @@ - - /* This header is needed here for HMAC-MD5, but it will be regenerated - * in ieee802_1x_send() */ -- hdr->version = EAPOL_VERSION; -+ hdr->version = hapd->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len); - hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len, -@@ -1665,7 +1665,7 @@ - "dot1xPaePortInitialize=%d\n" - "dot1xPaePortReauthenticate=FALSE\n", - sta->aid, -- EAPOL_VERSION, -+ hapd->conf->eapol_version, - sm->initialize); - - /* dot1xAuthConfigTable */ -Index: wpa.c -=================================================================== -RCS file: /home/ncvs/src/contrib/hostapd/wpa.c,v -retrieving revision 1.4 -diff -u -r1.4 wpa.c ---- wpa.c 7 Mar 2006 05:51:52 -0000 1.4 -+++ wpa.c 27 Mar 2006 05:28:12 -0000 -@@ -1855,7 +1855,7 @@ - if (hdr == NULL) - return; - memset(hdr, 0, len); -- hdr->version = EAPOL_VERSION; -+ hdr->version = hapd->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len - sizeof(*hdr)); - key = (struct wpa_eapol_key *) (hdr + 1); diff --git a/contrib/hostapd/hostapd.accept b/contrib/hostapd/hostapd.accept deleted file mode 100644 index 57122b6634497..0000000000000 --- a/contrib/hostapd/hostapd.accept +++ /dev/null @@ -1,5 +0,0 @@ -# List of MAC addresses that are allowed to authenticate (IEEE 802.11) -# with the AP. -00:11:22:33:44:55 -00:66:77:88:99:aa -00:00:22:33:44:55 diff --git a/contrib/hostapd/hostapd_ctrl.c b/contrib/hostapd/hostapd_ctrl.c deleted file mode 100644 index 55b690451b509..0000000000000 --- a/contrib/hostapd/hostapd_ctrl.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * hostapd - hostapd control interface library - * Copyright (c) 2004, 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/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/un.h> - -#include "hostapd_ctrl.h" - - -struct hostapd_ctrl { - int s; - struct sockaddr_un local; - struct sockaddr_un dest; -}; - - -struct hostapd_ctrl * hostapd_ctrl_open(const char *ctrl_path) -{ - struct hostapd_ctrl *ctrl; - static int counter = 0; - - ctrl = malloc(sizeof(*ctrl)); - if (ctrl == NULL) - return NULL; - memset(ctrl, 0, sizeof(*ctrl)); - - ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); - if (ctrl->s < 0) { - free(ctrl); - return NULL; - } - - ctrl->local.sun_family = AF_UNIX; - snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/hostapd_ctrl_%d-%d", getpid(), counter++); - if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, - sizeof(ctrl->local.sun_family) + - strlen(ctrl->local.sun_path)) < 0) { - close(ctrl->s); - free(ctrl); - return NULL; - } - - ctrl->dest.sun_family = AF_UNIX; - strncpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path)); - if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, - sizeof(ctrl->dest.sun_family) + - strlen(ctrl->dest.sun_path)) < 0) { - close(ctrl->s); - unlink(ctrl->local.sun_path); - free(ctrl); - return NULL; - } - - return ctrl; -} - - -void hostapd_ctrl_close(struct hostapd_ctrl *ctrl) -{ - unlink(ctrl->local.sun_path); - close(ctrl->s); - free(ctrl); -} - - -int hostapd_ctrl_request(struct hostapd_ctrl *ctrl, char *cmd, size_t cmd_len, - char *reply, size_t *reply_len, - void (*msg_cb)(char *msg, size_t len)) -{ - struct timeval tv; - int res; - fd_set rfds; - - if (send(ctrl->s, cmd, cmd_len, 0) < 0) - return -1; - - for (;;) { - tv.tv_sec = 2; - tv.tv_usec = 0; - FD_ZERO(&rfds); - FD_SET(ctrl->s, &rfds); - res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); - if (FD_ISSET(ctrl->s, &rfds)) { - res = recv(ctrl->s, reply, *reply_len, 0); - if (res < 0) - return res; - if (res > 0 && reply[0] == '<') { - /* This is an unsolicited message from - * wpa_supplicant, not the reply to the - * request. Use msg_cb to report this to the - * caller. */ - if (msg_cb) { - /* Make sure the message is nul - * terminated. */ - if (res == *reply_len) - res = (*reply_len) - 1; - reply[res] = '\0'; - msg_cb(reply, res); - } - continue; - } - *reply_len = res; - break; - } else { - return -2; - } - } - return 0; -} - - -static int hostapd_ctrl_attach_helper(struct hostapd_ctrl *ctrl, int attach) -{ - char buf[10]; - int ret; - size_t len = 10; - - ret = hostapd_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, - buf, &len, NULL); - if (ret < 0) - return ret; - if (len == 3 && memcmp(buf, "OK\n", 3) == 0) - return 0; - return -1; -} - - -int hostapd_ctrl_attach(struct hostapd_ctrl *ctrl) -{ - return hostapd_ctrl_attach_helper(ctrl, 1); -} - - -int hostapd_ctrl_detach(struct hostapd_ctrl *ctrl) -{ - return hostapd_ctrl_attach_helper(ctrl, 0); -} - - -int hostapd_ctrl_recv(struct hostapd_ctrl *ctrl, char *reply, - size_t *reply_len) -{ - int res; - - res = recv(ctrl->s, reply, *reply_len, 0); - if (res < 0) - return res; - *reply_len = res; - return 0; -} - - -int hostapd_ctrl_pending(struct hostapd_ctrl *ctrl) -{ - struct timeval tv; - int res; - fd_set rfds; - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&rfds); - FD_SET(ctrl->s, &rfds); - res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); - return FD_ISSET(ctrl->s, &rfds); -} - - -int hostapd_ctrl_get_fd(struct hostapd_ctrl *ctrl) -{ - return ctrl->s; -} diff --git a/contrib/hostapd/hostapd_ctrl.h b/contrib/hostapd/hostapd_ctrl.h deleted file mode 100644 index 7ba221e19fc14..0000000000000 --- a/contrib/hostapd/hostapd_ctrl.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef HOSTAPD_CTRL_H -#define HOSTAPD_CTRL_H - -struct hostapd_ctrl; - -struct hostapd_ctrl * hostapd_ctrl_open(const char *ctrl_path); -void hostapd_ctrl_close(struct hostapd_ctrl *ctrl); -int hostapd_ctrl_request(struct hostapd_ctrl *ctrl, char *cmd, size_t cmd_len, - char *reply, size_t *reply_len, - void (*msg_cb)(char *msg, size_t len)); -int hostapd_ctrl_attach(struct hostapd_ctrl *ctrl); -int hostapd_ctrl_detach(struct hostapd_ctrl *ctrl); -int hostapd_ctrl_recv(struct hostapd_ctrl *ctrl, char *reply, - size_t *reply_len); -int hostapd_ctrl_pending(struct hostapd_ctrl *ctrl); -int hostapd_ctrl_get_fd(struct hostapd_ctrl *ctrl); - -#endif /* HOSTAPD_CTRL_H */ diff --git a/contrib/hostapd/hw_features.c b/contrib/hostapd/hw_features.c deleted file mode 100644 index 484959f63cc5c..0000000000000 --- a/contrib/hostapd/hw_features.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * hostapd / Hardware feature query and different modes - * Copyright 2002-2003, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "hw_features.h" -#include "driver.h" -#include "config.h" -#include "ieee802_11.h" -#include "eloop.h" - - -void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features) -{ - size_t i; - - if (hw_features == NULL) - return; - - for (i = 0; i < num_hw_features; i++) { - free(hw_features[i].channels); - free(hw_features[i].rates); - } - - free(hw_features); -} - - -int hostapd_get_hw_features(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - int ret = 0, i, j; - u16 num_modes, flags; - struct hostapd_hw_modes *modes; - - modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); - if (modes == NULL) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Fetching hardware channel/rate support not " - "supported."); - return -1; - } - - iface->hw_flags = flags; - - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = modes; - iface->num_hw_features = num_modes; - - for (i = 0; i < num_modes; i++) { - struct hostapd_hw_modes *feature = &modes[i]; - /* set flag for channels we can use in current regulatory - * domain */ - for (j = 0; j < feature->num_channels; j++) { - /* TODO: add regulatory domain lookup */ - unsigned char power_level = 0; - unsigned char antenna_max = 0; - - if ((feature->mode == HOSTAPD_MODE_IEEE80211G || - feature->mode == HOSTAPD_MODE_IEEE80211B) && - feature->channels[j].chan >= 1 && - feature->channels[j].chan <= 11) { - power_level = 20; - feature->channels[j].flag |= - HOSTAPD_CHAN_W_SCAN; - } else - feature->channels[j].flag &= - ~HOSTAPD_CHAN_W_SCAN; - - hostapd_set_channel_flag(hapd, feature->mode, - feature->channels[j].chan, - feature->channels[j].flag, - power_level, - antenna_max); - } - } - - return ret; -} - - -static int hostapd_prepare_rates(struct hostapd_data *hapd, - struct hostapd_hw_modes *mode) -{ - int i, num_basic_rates = 0; - int basic_rates_a[] = { 60, 120, 240, -1 }; - int basic_rates_b[] = { 10, 20, -1 }; - int basic_rates_g[] = { 10, 20, 55, 110, -1 }; - int *basic_rates; - - if (hapd->iconf->basic_rates) - basic_rates = hapd->iconf->basic_rates; - else switch (mode->mode) { - case HOSTAPD_MODE_IEEE80211A: - basic_rates = basic_rates_a; - break; - case HOSTAPD_MODE_IEEE80211B: - basic_rates = basic_rates_b; - break; - case HOSTAPD_MODE_IEEE80211G: - basic_rates = basic_rates_g; - break; - default: - return -1; - } - - if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, - basic_rates, mode->mode)) { - printf("Failed to update rate sets in kernel module\n"); - } - - free(hapd->iface->current_rates); - hapd->iface->num_rates = 0; - - hapd->iface->current_rates = - malloc(mode->num_rates * sizeof(struct hostapd_rate_data)); - if (!hapd->iface->current_rates) { - printf("Failed to allocate memory for rate table.\n"); - return -1; - } - - for (i = 0; i < mode->num_rates; i++) { - struct hostapd_rate_data *rate; - - if (hapd->iconf->supported_rates && - !hostapd_rate_found(hapd->iconf->supported_rates, - mode->rates[i].rate)) - continue; - - rate = &hapd->iface->current_rates[hapd->iface->num_rates]; - memcpy(rate, &mode->rates[i], - sizeof(struct hostapd_rate_data)); - if (hostapd_rate_found(basic_rates, rate->rate)) { - rate->flags |= HOSTAPD_RATE_BASIC; - num_basic_rates++; - } else - rate->flags &= ~HOSTAPD_RATE_BASIC; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RATE[%d] rate=%d flags=0x%x\n", - hapd->iface->num_rates, rate->rate, rate->flags); - hapd->iface->num_rates++; - } - - if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { - printf("No rates remaining in supported/basic rate sets " - "(%d,%d).\n", hapd->iface->num_rates, num_basic_rates); - return -1; - } - - return 0; -} - - -static void select_hw_mode_start(void *eloop_data, void *user_ctx); -static void select_hw_mode2_handler(void *eloop_data, void *user_ctx); - -/** - * select_hw_mode_finalize - Finish select HW mode & call the callback - * @iface: Pointer to interface data. - * @status: Status of the select HW mode (0 on success; -1 on failure). - * Returns: 0 on success; -1 on failure (e.g., was not in progress). - */ -static int select_hw_mode_finalize(struct hostapd_iface *iface, int status) -{ - hostapd_iface_cb cb; - - if (!iface->hw_mode_sel_cb) - return -1; - - eloop_cancel_timeout(select_hw_mode_start, iface, NULL); - eloop_cancel_timeout(select_hw_mode2_handler, iface, NULL); - - cb = iface->hw_mode_sel_cb; - - iface->hw_mode_sel_cb = NULL; - - cb(iface, status); - - return 0; -} - - -/** - * select_hw_mode2 - Select the hardware mode (part 2) - * @iface: Pointer to interface data. - * @status: Status of auto chanel selection. - * - * Setup the rates and passive scanning based on the configuration. - */ -static void select_hw_mode2(struct hostapd_iface *iface, int status) -{ - int ret = status; - if (ret) - goto fail; - - if (iface->current_mode == NULL) { - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured channel"); - ret = -1; - goto fail; - } - - if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { - printf("Failed to prepare rates table.\n"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Failed to prepare rates table."); - ret = -1; - goto fail; - } - - ret = hostapd_passive_scan(iface->bss[0], 0, - iface->conf->passive_scan_mode, - iface->conf->passive_scan_interval, - iface->conf->passive_scan_listen, - NULL, NULL); - if (ret) { - printf("Could not set passive scanning: %s\n", strerror(ret)); - ret = 0; - } - -fail: - select_hw_mode_finalize(iface, ret); -} - - -/** - * select_hw_mode2_handler - Calls select_hw_mode2 when auto chan isn't used - * @eloop_data: Stores the struct hostapd_iface * for the interface. - * @user_ctx: Unused. - */ -static void select_hw_mode2_handler(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *iface = eloop_data; - - select_hw_mode2(iface, 0); -} - - -/** - * select_hw_mode1 - Select the hardware mode (part 1) - * @iface: Pointer to interface data. - * Returns: 0 on success; -1 on failure. - * - * Setup the hardware mode and channel based on the configuration. - * Schedules select_hw_mode2() to be called immediately or after automatic - * channel selection takes place. - */ -static int select_hw_mode1(struct hostapd_iface *iface) -{ - int i, j, ok; - - if (iface->num_hw_features < 1) - return -1; - - iface->current_mode = NULL; - for (i = 0; i < iface->num_hw_features; i++) { - struct hostapd_hw_modes *mode = &iface->hw_features[i]; - if (mode->mode == (int) iface->conf->hw_mode) { - iface->current_mode = mode; - break; - } - } - - if (iface->current_mode == NULL) { - printf("Hardware does not support configured mode\n"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured mode " - "(%d)", (int) iface->conf->hw_mode); - return -1; - } - - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if ((chan->flag & HOSTAPD_CHAN_W_SCAN) && - (chan->chan == iface->conf->channel)) { - ok = 1; - break; - } - } - if (ok == 0 && iface->conf->channel != 0) { - hostapd_logger(iface->bss[0], NULL, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Configured channel (%d) not found from the " - "channel list of current mode (%d) %s", - iface->conf->channel, - iface->current_mode->mode, - hostapd_hw_mode_txt(iface->current_mode->mode)); - iface->current_mode = NULL; - } - - /* - * Calls select_hw_mode2() via a handler, so that the function is - * always executed from eloop. - */ - eloop_register_timeout(0, 0, select_hw_mode2_handler, iface, NULL); - return 0; -} - - -/** - * select_hw_mode_start - Handler to start select HW mode - * @eloop_data: Stores the struct hostapd_iface * for the interface. - * @user_ctx: Unused. - * - * An eloop handler is used so that all errors can be processed by the - * callback without introducing stack recursion. - */ -static void select_hw_mode_start(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *iface = (struct hostapd_iface *)eloop_data; - - int ret; - - ret = select_hw_mode1(iface); - if (ret) - select_hw_mode_finalize(iface, ret); -} - - -/** - * hostapd_select_hw_mode_start - Start selection of the hardware mode - * @iface: Pointer to interface data. - * @cb: The function to callback when done. - * Returns: 0 if it starts successfully; cb will be called when done. - * -1 on failure; cb will not be called. - * - * Sets up the hardware mode, channel, rates, and passive scanning - * based on the configuration. - */ -int hostapd_select_hw_mode_start(struct hostapd_iface *iface, - hostapd_iface_cb cb) -{ - if (iface->hw_mode_sel_cb) { - wpa_printf(MSG_DEBUG, - "%s: Hardware mode select already in progress.", - iface->bss[0]->conf->iface); - return -1; - } - - iface->hw_mode_sel_cb = cb; - - eloop_register_timeout(0, 0, select_hw_mode_start, iface, NULL); - - return 0; -} - - -/** - * hostapd_auto_chan_select_stop - Stops automatic channel selection - * @iface: Pointer to interface data. - * Returns: 0 if successfully stopped; - * -1 on failure (i.e., was not in progress) - */ -int hostapd_select_hw_mode_stop(struct hostapd_iface *iface) -{ - return select_hw_mode_finalize(iface, -1); -} - - -const char * hostapd_hw_mode_txt(int mode) -{ - switch (mode) { - case HOSTAPD_MODE_IEEE80211A: - return "IEEE 802.11a"; - case HOSTAPD_MODE_IEEE80211B: - return "IEEE 802.11b"; - case HOSTAPD_MODE_IEEE80211G: - return "IEEE 802.11g"; - default: - return "UNKNOWN"; - } -} - - -int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) -{ - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->chan == chan) - return ch->freq; - } - - return 0; -} - - -int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) -{ - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->freq == freq) - return ch->chan; - } - - return 0; -} diff --git a/contrib/hostapd/ieee802_11.h b/contrib/hostapd/ieee802_11.h deleted file mode 100644 index 37bd711d0194d..0000000000000 --- a/contrib/hostapd/ieee802_11.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef IEEE802_11_H -#define IEEE802_11_H - -/* IEEE 802.11 defines */ - -#define WLAN_FC_PVER (BIT(1) | BIT(0)) -#define WLAN_FC_TODS BIT(8) -#define WLAN_FC_FROMDS BIT(9) -#define WLAN_FC_MOREFRAG BIT(10) -#define WLAN_FC_RETRY BIT(11) -#define WLAN_FC_PWRMGT BIT(12) -#define WLAN_FC_MOREDATA BIT(13) -#define WLAN_FC_ISWEP BIT(14) -#define WLAN_FC_ORDER BIT(15) - -#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) -#define WLAN_FC_GET_STYPE(fc) \ - (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) - -#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) -#define WLAN_GET_SEQ_SEQ(seq) \ - (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) - -#define WLAN_FC_TYPE_MGMT 0 -#define WLAN_FC_TYPE_CTRL 1 -#define WLAN_FC_TYPE_DATA 2 - -/* management */ -#define WLAN_FC_STYPE_ASSOC_REQ 0 -#define WLAN_FC_STYPE_ASSOC_RESP 1 -#define WLAN_FC_STYPE_REASSOC_REQ 2 -#define WLAN_FC_STYPE_REASSOC_RESP 3 -#define WLAN_FC_STYPE_PROBE_REQ 4 -#define WLAN_FC_STYPE_PROBE_RESP 5 -#define WLAN_FC_STYPE_BEACON 8 -#define WLAN_FC_STYPE_ATIM 9 -#define WLAN_FC_STYPE_DISASSOC 10 -#define WLAN_FC_STYPE_AUTH 11 -#define WLAN_FC_STYPE_DEAUTH 12 -#define WLAN_FC_STYPE_ACTION 13 - -/* control */ -#define WLAN_FC_STYPE_PSPOLL 10 -#define WLAN_FC_STYPE_RTS 11 -#define WLAN_FC_STYPE_CTS 12 -#define WLAN_FC_STYPE_ACK 13 -#define WLAN_FC_STYPE_CFEND 14 -#define WLAN_FC_STYPE_CFENDACK 15 - -/* data */ -#define WLAN_FC_STYPE_DATA 0 -#define WLAN_FC_STYPE_DATA_CFACK 1 -#define WLAN_FC_STYPE_DATA_CFPOLL 2 -#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 -#define WLAN_FC_STYPE_NULLFUNC 4 -#define WLAN_FC_STYPE_CFACK 5 -#define WLAN_FC_STYPE_CFPOLL 6 -#define WLAN_FC_STYPE_CFACKPOLL 7 -#define WLAN_FC_STYPE_QOS_DATA 8 - -/* Authentication algorithms */ -#define WLAN_AUTH_OPEN 0 -#define WLAN_AUTH_SHARED_KEY 1 - -#define WLAN_AUTH_CHALLENGE_LEN 128 - -#define WLAN_CAPABILITY_ESS BIT(0) -#define WLAN_CAPABILITY_IBSS BIT(1) -#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) -#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) -#define WLAN_CAPABILITY_PRIVACY BIT(4) -#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) -#define WLAN_CAPABILITY_PBCC BIT(6) -#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) -#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) -#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) -#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) - -/* Status codes */ -#define WLAN_STATUS_SUCCESS 0 -#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 -#define WLAN_STATUS_CAPS_UNSUPPORTED 10 -#define WLAN_STATUS_REASSOC_NO_ASSOC 11 -#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 -#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 -#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 -#define WLAN_STATUS_CHALLENGE_FAIL 15 -#define WLAN_STATUS_AUTH_TIMEOUT 16 -#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 -#define WLAN_STATUS_ASSOC_DENIED_RATES 18 -/* IEEE 802.11b */ -#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 -#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 -#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 -/* IEEE 802.11h */ -#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 -#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 -#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 -/* IEEE 802.11i */ -#define WLAN_STATUS_INVALID_IE 40 -#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 -#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 -#define WLAN_STATUS_AKMP_NOT_VALID 43 -#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 -#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 -#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 - -/* Reason codes */ -#define WLAN_REASON_UNSPECIFIED 1 -#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 -#define WLAN_REASON_DEAUTH_LEAVING 3 -#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 -#define WLAN_REASON_DISASSOC_AP_BUSY 5 -#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 -#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 -#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 -#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 -/* IEEE 802.11i */ -#define WLAN_REASON_INVALID_IE 13 -#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 -#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 -#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 -#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 -#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 -#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 -#define WLAN_REASON_AKMP_NOT_VALID 20 -#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 -#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 -#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 -#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 - - -/* Information Element IDs */ -#define WLAN_EID_SSID 0 -#define WLAN_EID_SUPP_RATES 1 -#define WLAN_EID_FH_PARAMS 2 -#define WLAN_EID_DS_PARAMS 3 -#define WLAN_EID_CF_PARAMS 4 -#define WLAN_EID_TIM 5 -#define WLAN_EID_IBSS_PARAMS 6 -#define WLAN_EID_COUNTRY 7 -#define WLAN_EID_CHALLENGE 16 -/* EIDs defined by IEEE 802.11h - START */ -#define WLAN_EID_PWR_CONSTRAINT 32 -#define WLAN_EID_PWR_CAPABILITY 33 -#define WLAN_EID_TPC_REQUEST 34 -#define WLAN_EID_TPC_REPORT 35 -#define WLAN_EID_SUPPORTED_CHANNELS 36 -#define WLAN_EID_CHANNEL_SWITCH 37 -#define WLAN_EID_MEASURE_REQUEST 38 -#define WLAN_EID_MEASURE_REPORT 39 -#define WLAN_EID_QUITE 40 -#define WLAN_EID_IBSS_DFS 41 -/* EIDs defined by IEEE 802.11h - END */ -#define WLAN_EID_ERP_INFO 42 -#define WLAN_EID_RSN 48 -#define WLAN_EID_EXT_SUPP_RATES 50 -#define WLAN_EID_GENERIC 221 -#define WLAN_EID_VENDOR_SPECIFIC 221 - - -struct ieee80211_mgmt { - u16 frame_control; - u16 duration; - u8 da[6]; - u8 sa[6]; - u8 bssid[6]; - u16 seq_ctrl; - union { - struct { - u16 auth_alg; - u16 auth_transaction; - u16 status_code; - /* possibly followed by Challenge text */ - u8 variable[0]; - } __attribute__ ((packed)) auth; - struct { - u16 reason_code; - } __attribute__ ((packed)) deauth; - struct { - u16 capab_info; - u16 listen_interval; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) assoc_req; - struct { - u16 capab_info; - u16 status_code; - u16 aid; - /* followed by Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) assoc_resp, reassoc_resp; - struct { - u16 capab_info; - u16 listen_interval; - u8 current_ap[6]; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) reassoc_req; - struct { - u16 reason_code; - } __attribute__ ((packed)) disassoc; - struct { - /* only variable items: SSID, Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) probe_req; - struct { - u8 timestamp[8]; - u16 beacon_int; - u16 capab_info; - /* followed by some of SSID, Supported rates, - * FH Params, DS Params, CF Params, IBSS Params */ - u8 variable[0]; - } __attribute__ ((packed)) probe_resp; - struct { - u8 timestamp[8]; - u16 beacon_int; - u16 capab_info; - /* followed by some of SSID, Supported rates, - * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; - } __attribute__ ((packed)) beacon; - struct { - u8 category; - union { - struct { - u8 action_code; - u8 dialog_token; - u8 status_code; - u8 variable[0]; - } __attribute__ ((packed)) wme_action; - struct{ - u8 action_code; - u8 element_id; - u8 length; - u8 switch_mode; - u8 new_chan; - u8 switch_count; - } __attribute__ ((packed)) chan_switch; - } u; - } __attribute__ ((packed)) action; - } u; -} __attribute__ ((packed)); - - -#define ERP_INFO_NON_ERP_PRESENT BIT(0) -#define ERP_INFO_USE_PROTECTION BIT(1) -#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) - -/* Parsed Information Elements */ -struct ieee802_11_elems { - u8 *ssid; - u8 ssid_len; - u8 *supp_rates; - u8 supp_rates_len; - u8 *fh_params; - u8 fh_params_len; - u8 *ds_params; - u8 ds_params_len; - u8 *cf_params; - u8 cf_params_len; - u8 *tim; - u8 tim_len; - u8 *ibss_params; - u8 ibss_params_len; - u8 *challenge; - u8 challenge_len; - u8 *erp_info; - u8 erp_info_len; - u8 *ext_supp_rates; - u8 ext_supp_rates_len; - u8 *wpa_ie; - u8 wpa_ie_len; - u8 *rsn_ie; - u8 rsn_ie_len; - u8 *wme; - u8 wme_len; - u8 *wme_tspec; - u8 wme_tspec_len; - u8 *power_cap; - u8 power_cap_len; - u8 *supp_channels; - u8 supp_channels_len; -}; - -typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; - - -struct hostapd_frame_info { - u32 phytype; - u32 channel; - u32 datarate; - u32 ssi_signal; - - unsigned int passive_scan:1; -}; - - -void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason); -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, struct hostapd_frame_info *fi); -void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, int ok); -ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, - size_t len, - struct ieee802_11_elems *elems, - int show_errors); -void ieee802_11_print_ssid(const u8 *ssid, u8 len); -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, - int local); -int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe); -u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); - -#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd/madwifi.conf b/contrib/hostapd/madwifi.conf deleted file mode 100644 index ed3704fcb6346..0000000000000 --- a/contrib/hostapd/madwifi.conf +++ /dev/null @@ -1,278 +0,0 @@ -##### hostapd configuration file ############################################## -# Empty lines and lines starting with # are ignored - -# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for -# management frames) -interface=ath0 - -# In case of madwifi driver, an additional configuration parameter, bridge, -# must be used to notify hostapd if the interface is included in a bridge. This -# parameter is not used with Host AP driver. -bridge=br0 - -# Driver interface type (hostap/wired/madwifi; default: hostap) -driver=madwifi - -# hostapd event logger configuration -# -# Two output method: syslog and stdout (only usable if not forking to -# background). -# -# Module bitfield (ORed bitfield of modules that will be logged; -1 = all -# modules): -# bit 0 (1) = IEEE 802.11 -# bit 1 (2) = IEEE 802.1X -# bit 2 (4) = RADIUS -# bit 3 (8) = WPA -# bit 4 (16) = driver interface -# bit 5 (32) = IAPP -# -# Levels (minimum value for logged events): -# 0 = verbose debugging -# 1 = debugging -# 2 = informational messages -# 3 = notification -# 4 = warning -# -logger_syslog=-1 -logger_syslog_level=2 -logger_stdout=-1 -logger_stdout_level=2 - -# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive -debug=0 - -# Dump file for state information (on SIGUSR1) -dump_file=/tmp/hostapd.dump - - -##### IEEE 802.11 related configuration ####################################### - -# SSID to be used in IEEE 802.11 management frames -ssid=wpa-test - -##### IEEE 802.1X-REV related configuration ################################### - -# Require IEEE 802.1X authorization -#ieee8021x=1 - -# Optional displayable message sent with EAP Request-Identity. The first \0 -# in this string will be converted to ASCII-0 (nul). This can be used to -# separate network info (comma separated list of attribute=value pairs); see, -# e.g., RFC 4284. -#eap_message=hello -#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com - -# WEP rekeying (disabled if key lengths are not set or are set to 0) -# Key lengths for default/broadcast and individual/unicast keys: -# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) -# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) -#wep_key_len_broadcast=5 -#wep_key_len_unicast=5 -# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) -#wep_rekey_period=300 - -# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if -# only broadcast keys are used) -eapol_key_index_workaround=0 - -# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable -# reauthentication). -#eap_reauth_period=3600 - - -##### Integrated EAP server ################################################### - -# Optionally, hostapd can be configured to use an integrated EAP server -# to process EAP authentication locally without need for an external RADIUS -# server. This functionality can be used both as a local authentication server -# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. - -# Use integrated EAP server instead of external RADIUS authentication -# server. This is also needed if hostapd is configured to act as a RADIUS -# authentication server. -eap_server=0 - -# Path for EAP server user database -#eap_user_file=/etc/hostapd.eap_user - -# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#ca_cert=/etc/hostapd.ca.pem - -# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#server_cert=/etc/hostapd.server.pem - -# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS -# This may point to the same file as server_cert if both certificate and key -# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be -# used by commenting out server_cert and specifying the PFX file as the -# private_key. -#private_key=/etc/hostapd.server.prv - -# Passphrase for private key -#private_key_passwd=secret passphrase - -# Enable CRL verification. -# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a -# valid CRL signed by the CA is required to be included in the ca_cert file. -# This can be done by using PEM format for CA certificate and CRL and -# concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. -# 0 = do not verify CRLs (default) -# 1 = check the CRL of the user certificate -# 2 = check all CRLs in the certificate path -#check_crl=1 - -# Configuration data for EAP-SIM database/authentication gateway interface. -# This is a text string in implementation specific format. The example -# implementation in eap_sim_db.c uses this as the file name for the GSM -# authentication triplets. -#eap_sim_db=/etc/hostapd.sim_db - - -##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### - -# Interface to be used for IAPP broadcast packets -#iapp_interface=eth0 - - -##### RADIUS client configuration ############################################# -# for IEEE 802.1X with external Authentication Server, IEEE 802.11 -# authentication with external ACL for MAC addresses, and accounting - -# The own IP address of the access point (used as NAS-IP-Address) -own_ip_addr=127.0.0.1 - -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. -#nas_identifier=ap.example.com - -# RADIUS authentication server -#auth_server_addr=127.0.0.1 -#auth_server_port=1812 -#auth_server_shared_secret=secret - -# RADIUS accounting server -#acct_server_addr=127.0.0.1 -#acct_server_port=1813 -#acct_server_shared_secret=secret - -# Secondary RADIUS servers; to be used if primary one does not reply to -# RADIUS packets. These are optional and there can be more than one secondary -# server listed. -#auth_server_addr=127.0.0.2 -#auth_server_port=1812 -#auth_server_shared_secret=secret2 -# -#acct_server_addr=127.0.0.2 -#acct_server_port=1813 -#acct_server_shared_secret=secret2 - -# Retry interval for trying to return to the primary RADIUS server (in -# seconds). RADIUS client code will automatically try to use the next server -# when the current server is not replying to requests. If this interval is set, -# primary server will be retried after configured amount of time even if the -# currently used secondary server is still working. -#radius_retry_primary_interval=600 - - -# Interim accounting update interval -# If this is set (larger than 0) and acct_server is configured, hostapd will -# send interim accounting updates every N seconds. Note: if set, this overrides -# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this -# value should not be configured in hostapd.conf, if RADIUS server is used to -# control the interim interval. -# This value should not be less 600 (10 minutes) and must not be less than -# 60 (1 minute). -#radius_acct_interim_interval=600 - - -##### RADIUS authentication server configuration ############################## - -# hostapd can be used as a RADIUS authentication server for other hosts. This -# requires that the integrated EAP authenticator is also enabled and both -# authentication services are sharing the same configuration. - -# File name of the RADIUS clients configuration for the RADIUS server. If this -# commented out, RADIUS server is disabled. -#radius_server_clients=/etc/hostapd.radius_clients - -# The UDP port number for the RADIUS authentication server -#radius_server_auth_port=1812 - -# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) -#radius_server_ipv6=1 - - -##### WPA/IEEE 802.11i configuration ########################################## - -# Enable WPA. Setting this variable configures the AP to require WPA (either -# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either -# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. -# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), -# RADIUS authentication server must be configured, and WPA-EAP must be included -# in wpa_key_mgmt. -# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) -# and/or WPA2 (full IEEE 802.11i/RSN): -# bit0 = WPA -# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -#wpa=1 - -# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit -# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase -# (8..63 characters) that will be converted to PSK. This conversion uses SSID -# so the PSK changes when ASCII passphrase is used and the SSID is changed. -# wpa_psk (dot11RSNAConfigPSKValue) -# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) -#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -#wpa_passphrase=secret passphrase - -# Optionally, WPA PSKs can be read from a separate text file (containing list -# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. -# Use absolute path name to make sure that the files can be read on SIGHUP -# configuration reloads. -#wpa_psk_file=/etc/hostapd.wpa_psk - -# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The -# entries are separated with a space. -# (dot11RSNAConfigAuthenticationSuitesTable) -#wpa_key_mgmt=WPA-PSK WPA-EAP - -# Set of accepted cipher suites (encryption algorithms) for pairwise keys -# (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] -# Group cipher suite (encryption algorithm for broadcast and multicast frames) -# is automatically selected based on this configuration. If only CCMP is -# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. -# (dot11RSNAConfigPairwiseCiphersTable) -#wpa_pairwise=TKIP CCMP - -# Time interval for rekeying GTK (broadcast/multicast encryption keys) in -# seconds. (dot11RSNAConfigGroupRekeyTime) -#wpa_group_rekey=600 - -# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. -# (dot11RSNAConfigGroupRekeyStrict) -#wpa_strict_rekey=1 - -# Time interval for rekeying GMK (master key used internally to generate GTKs -# (in seconds). -#wpa_gmk_rekey=86400 - -# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up -# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN -# authentication and key handshake before actually associating with a new AP. -# (dot11RSNAPreauthenticationEnabled) -#rsn_preauth=1 -# -# Space separated list of interfaces from which pre-authentication frames are -# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all -# interface that are used for connections to other APs. This could include -# wired interfaces and WDS links. The normal wireless data interface towards -# associated stations (e.g., wlan0) should not be added, since -# pre-authentication is only used with APs other than the currently associated -# one. -#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd/reconfig.c b/contrib/hostapd/reconfig.c deleted file mode 100644 index a0d61569812bc..0000000000000 --- a/contrib/hostapd/reconfig.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * hostapd / Configuration reloading - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> - * Copyright (c) 2002-2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "beacon.h" -#include "hw_features.h" -#include "driver.h" -#include "sta_info.h" -#include "radius_client.h" -#include "ieee802_11.h" -#include "iapp.h" -#include "ap_list.h" -#include "wpa.h" -#include "vlan_init.h" -#include "ieee802_11_auth.h" -#include "ieee802_1x.h" -#include "accounting.h" -#include "eloop.h" - - -/** - * struct hostapd_config_change - Configuration change information - * This is for two purposes: - * - Storing configuration information in the hostapd_iface during - * the asynchronous parts of reconfiguration. - * - Passing configuration information for per-station reconfiguration. - */ -struct hostapd_config_change { - struct hostapd_data *hapd; - struct hostapd_config *newconf, *oldconf; - struct hostapd_bss_config *newbss, *oldbss; - int mac_acl_changed; - int num_sta_remove; /* number of STAs that need to be removed */ - int beacon_changed; - struct hostapd_iface *hapd_iface; - struct hostapd_data **new_hapd, **old_hapd; - int num_old_hapd; -}; - - -static int hostapd_config_reload_sta(struct hostapd_data *hapd, - struct sta_info *sta, void *data) -{ - struct hostapd_config_change *change = data; - struct hostapd_bss_config *newbss, *oldbss; - int deauth = 0; - u8 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; - - newbss = change->newbss; - oldbss = change->oldbss; - hapd = change->hapd; - - if (sta->ssid == &oldbss->ssid) { - sta->ssid = &newbss->ssid; - - if (newbss->ssid.ssid_len != oldbss->ssid.ssid_len || - memcmp(newbss->ssid.ssid, oldbss->ssid.ssid, - newbss->ssid.ssid_len) != 0) { - /* main SSID was changed - kick STA out */ - deauth++; - } - } - sta->ssid_probe = sta->ssid; - - /* - * If MAC ACL configuration has changed, deauthenticate stations that - * have been removed from accepted list or have been added to denied - * list. If external RADIUS server is used for ACL, all stations are - * deauthenticated and they will need to authenticate again. This - * limits sudden load on the RADIUS server since the verification will - * be done over the time needed for the STAs to reauthenticate - * themselves. - */ - if (change->mac_acl_changed && - (newbss->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH || - !hostapd_allowed_address(hapd, sta->addr, NULL, 0, NULL, NULL, - NULL))) - deauth++; - - if (newbss->ieee802_1x != oldbss->ieee802_1x && - sta->ssid == &hapd->conf->ssid) - deauth++; - - if (newbss->wpa != oldbss->wpa) - deauth++; - - if (!newbss->wme_enabled && (sta->flags & WLAN_STA_WME)) - deauth++; - - if (newbss->auth_algs != oldbss->auth_algs && - ((sta->auth_alg == WLAN_AUTH_OPEN && - !(newbss->auth_algs & HOSTAPD_AUTH_OPEN)) || - (sta->auth_alg == WLAN_AUTH_SHARED_KEY && - !(newbss->auth_algs & HOSTAPD_AUTH_SHARED_KEY)))) - deauth++; - - if (change->num_sta_remove > 0) { - deauth++; - reason = WLAN_REASON_DISASSOC_AP_BUSY; - } - - if (deauth) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "STA " MACSTR - " deauthenticated during config reloading " - "(reason=%d)\n", MAC2STR(sta->addr), reason); - ieee802_11_send_deauth(hapd, sta->addr, reason); - ap_sta_deauthenticate(hapd, sta, reason); - change->num_sta_remove--; - } - - return 0; -} - - -static void hostapd_reconfig_tx_queue_params(struct hostapd_data *hapd, - struct hostapd_config *newconf, - struct hostapd_config *oldconf) -{ - int i; - struct hostapd_tx_queue_params *o, *n; - - for (i = 0; i < NUM_TX_QUEUES; i++) { - o = &oldconf->tx_queue[i]; - n = &newconf->tx_queue[i]; - - if (!n->configured) - continue; - - if ((n->aifs != o->aifs || n->cwmin != o->cwmin || - n->cwmax != o->cwmax || n->burst != o->burst) && - hostapd_set_tx_queue_params(hapd, i, n->aifs, n->cwmin, - n->cwmax, n->burst)) - printf("Failed to set TX queue parameters for queue %d" - ".\n", i); - } -} - - -static int hostapd_reconfig_wme(struct hostapd_data *hapd, - struct hostapd_config *newconf, - struct hostapd_config *oldconf) -{ - int beacon_changed = 0; - size_t i; - struct hostapd_wme_ac_params *o, *n; - - for (i = 0; i < sizeof(newconf->wme_ac_params) / - sizeof(newconf->wme_ac_params[0]); i++) { - o = &oldconf->wme_ac_params[i]; - n = &newconf->wme_ac_params[i]; - if (n->cwmin != o->cwmin || - n->cwmax != o->cwmax || - n->aifs != o->aifs || - n->txopLimit != o->txopLimit || - n->admission_control_mandatory != - o->admission_control_mandatory) { - beacon_changed++; - hapd->parameter_set_count++; - } - } - - return beacon_changed; -} - - -static int rate_array_diff(int *a1, int *a2) -{ - int i; - - if (a1 == NULL && a2 == NULL) - return 0; - if (a1 == NULL || a2 == NULL) - return 1; - - i = 0; - for (;;) { - if (a1[i] != a2[i]) - return 1; - if (a1[i] == -1) - break; - i++; - } - - return 0; -} - - -static int hostapd_acl_diff(struct hostapd_bss_config *a, - struct hostapd_bss_config *b) -{ - int i; - - if (a->macaddr_acl != b->macaddr_acl || - a->num_accept_mac != b->num_accept_mac || - a->num_deny_mac != b->num_deny_mac) - return 1; - - for (i = 0; i < a->num_accept_mac; i++) { - if (memcmp(a->accept_mac[i], b->accept_mac[i], ETH_ALEN) != 0) - return 1; - } - - for (i = 0; i < a->num_deny_mac; i++) { - if (memcmp(a->deny_mac[i], b->deny_mac[i], ETH_ALEN) != 0) - return 1; - } - - return 0; -} - - -/** - * reload_iface2 - Part 2 of reload_iface - * @hapd_iface: Pointer to hostapd interface data. - */ -static void reload_iface2(struct hostapd_iface *hapd_iface) -{ - struct hostapd_data *hapd = hapd_iface->bss[0]; - struct hostapd_config *newconf = hapd_iface->change->newconf; - struct hostapd_config *oldconf = hapd_iface->change->oldconf; - int beacon_changed = hapd_iface->change->beacon_changed; - hostapd_iface_cb cb = hapd_iface->reload_iface_cb; - - if (newconf->preamble != oldconf->preamble) { - if (hostapd_set_preamble(hapd, hapd->iconf->preamble)) - printf("Could not set preamble for kernel driver\n"); - beacon_changed++; - } - - if (newconf->beacon_int != oldconf->beacon_int) { - /* Need to change beacon interval if it has changed or if - * auto channel selection was used. */ - if (hostapd_set_beacon_int(hapd, newconf->beacon_int)) - printf("Could not set beacon interval for kernel " - "driver\n"); - if (newconf->beacon_int != oldconf->beacon_int) - beacon_changed++; - } - - if (newconf->cts_protection_type != oldconf->cts_protection_type) - beacon_changed++; - - if (newconf->rts_threshold > -1 && - newconf->rts_threshold != oldconf->rts_threshold && - hostapd_set_rts(hapd, newconf->rts_threshold)) - printf("Could not set RTS threshold for kernel driver\n"); - - if (newconf->fragm_threshold > -1 && - newconf->fragm_threshold != oldconf->fragm_threshold && - hostapd_set_frag(hapd, newconf->fragm_threshold)) - printf("Could not set fragmentation threshold for kernel " - "driver\n"); - - hostapd_reconfig_tx_queue_params(hapd, newconf, oldconf); - - if (hostapd_reconfig_wme(hapd, newconf, oldconf) > 0) - beacon_changed++; - - ap_list_reconfig(hapd_iface, oldconf); - - hapd_iface->change->beacon_changed = beacon_changed; - - hapd_iface->reload_iface_cb = NULL; - cb(hapd_iface, 0); -} - - -/** - * reload_iface2_handler - Handler that calls reload_face2 - * @eloop_data: Stores the struct hostapd_iface for the interface. - * @user_ctx: Unused. - */ -static void reload_iface2_handler(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *hapd_iface = eloop_data; - - reload_iface2(hapd_iface); -} - - -/** - * reload_hw_mode_done - Callback for after the HW mode is setup - * @hapd_iface: Pointer to interface data. - * @status: Status of the HW mode setup. - */ -static void reload_hw_mode_done(struct hostapd_iface *hapd_iface, int status) -{ - struct hostapd_data *hapd = hapd_iface->bss[0]; - struct hostapd_config_change *change = hapd_iface->change; - struct hostapd_config *newconf = change->newconf; - hostapd_iface_cb cb; - int freq; - - if (status) { - printf("Failed to select hw_mode.\n"); - - cb = hapd_iface->reload_iface_cb; - hapd_iface->reload_iface_cb = NULL; - cb(hapd_iface, -1); - - return; - } - - freq = hostapd_hw_get_freq(hapd, newconf->channel); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Mode: %s Channel: %d Frequency: %d MHz\n", - hostapd_hw_mode_txt(newconf->hw_mode), - newconf->channel, freq); - - if (hostapd_set_freq(hapd, newconf->hw_mode, freq)) { - printf("Could not set channel %d (%d MHz) for kernel " - "driver\n", newconf->channel, freq); - } - - change->beacon_changed++; - - reload_iface2(hapd_iface); -} - - -/** - * hostapd_config_reload_iface_start - Start interface reload - * @hapd_iface: Pointer to interface data. - * @cb: The function to callback when done. - * Returns: 0 if it starts successfully; cb will be called when done. - * -1 on failure; cb will not be called. - */ -static int hostapd_config_reload_iface_start(struct hostapd_iface *hapd_iface, - hostapd_iface_cb cb) -{ - struct hostapd_config_change *change = hapd_iface->change; - struct hostapd_config *newconf = change->newconf; - struct hostapd_config *oldconf = change->oldconf; - struct hostapd_data *hapd = hapd_iface->bss[0]; - - if (hapd_iface->reload_iface_cb) { - wpa_printf(MSG_DEBUG, - "%s: Interface reload already in progress.", - hapd_iface->bss[0]->conf->iface); - return -1; - } - - hapd_iface->reload_iface_cb = cb; - - if (newconf->bridge_packets != oldconf->bridge_packets && - hapd->iconf->bridge_packets != INTERNAL_BRIDGE_DO_NOT_CONTROL && - hostapd_set_internal_bridge(hapd, hapd->iconf->bridge_packets)) - printf("Failed to set bridge_packets for kernel driver\n"); - - if (newconf->channel != oldconf->channel || - newconf->hw_mode != oldconf->hw_mode || - rate_array_diff(newconf->supported_rates, - oldconf->supported_rates) || - rate_array_diff(newconf->basic_rates, oldconf->basic_rates)) { - hostapd_free_stas(hapd); - - if (hostapd_get_hw_features(hapd_iface)) { - printf("Could not read HW feature info from the kernel" - " driver.\n"); - hapd_iface->reload_iface_cb = NULL; - return -1; - } - - if (hostapd_select_hw_mode_start(hapd_iface, - reload_hw_mode_done)) { - printf("Failed to start select hw_mode.\n"); - hapd_iface->reload_iface_cb = NULL; - return -1; - } - - return 0; - } - - eloop_register_timeout(0, 0, reload_iface2_handler, hapd_iface, NULL); - return 0; -} - - -static void hostapd_reconfig_bss(struct hostapd_data *hapd, - struct hostapd_bss_config *newbss, - struct hostapd_bss_config *oldbss, - struct hostapd_config *oldconf, - int beacon_changed) -{ - struct hostapd_config_change change; - int encr_changed = 0; - struct radius_client_data *old_radius; - - radius_client_flush(hapd->radius, 0); - - if (hostapd_set_dtim_period(hapd, newbss->dtim_period)) - printf("Could not set DTIM period for kernel driver\n"); - - if (newbss->ssid.ssid_len != oldbss->ssid.ssid_len || - memcmp(newbss->ssid.ssid, oldbss->ssid.ssid, - newbss->ssid.ssid_len) != 0) { - if (hostapd_set_ssid(hapd, (u8 *) newbss->ssid.ssid, - newbss->ssid.ssid_len)) - printf("Could not set SSID for kernel driver\n"); - beacon_changed++; - } - - if (newbss->ignore_broadcast_ssid != oldbss->ignore_broadcast_ssid) - beacon_changed++; - - if (hostapd_wep_key_cmp(&newbss->ssid.wep, &oldbss->ssid.wep)) { - encr_changed++; - beacon_changed++; - } - - vlan_reconfig(hapd, oldconf, oldbss); - - if (beacon_changed) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Updating beacon frame " - "information\n"); - ieee802_11_set_beacon(hapd); - } - - change.hapd = hapd; - change.oldconf = oldconf; - change.newconf = hapd->iconf; - change.oldbss = oldbss; - change.newbss = newbss; - change.mac_acl_changed = hostapd_acl_diff(newbss, oldbss); - if (newbss->max_num_sta != oldbss->max_num_sta && - newbss->max_num_sta < hapd->num_sta) { - change.num_sta_remove = hapd->num_sta - newbss->max_num_sta; - } else - change.num_sta_remove = 0; - ap_for_each_sta(hapd, hostapd_config_reload_sta, &change); - - old_radius = hapd->radius; - hapd->radius = radius_client_reconfig(hapd->radius, hapd, - oldbss->radius, newbss->radius); - hapd->radius_client_reconfigured = old_radius != hapd->radius || - hostapd_ip_diff(&newbss->own_ip_addr, &oldbss->own_ip_addr); - - ieee802_1x_reconfig(hapd, oldconf, oldbss); - iapp_reconfig(hapd, oldconf, oldbss); - - hostapd_acl_reconfig(hapd, oldconf); - accounting_reconfig(hapd, oldconf); -} - - -/** - * config_reload2 - Part 2 of configuration reloading - * @hapd_iface: - */ -static void config_reload2(struct hostapd_iface *hapd_iface, int status) -{ - struct hostapd_config_change *change = hapd_iface->change; - struct hostapd_data *hapd = change->hapd; - struct hostapd_config *newconf = change->newconf; - struct hostapd_config *oldconf = change->oldconf; - int beacon_changed = change->beacon_changed; - struct hostapd_data **new_hapd = change->new_hapd; - struct hostapd_data **old_hapd = change->old_hapd; - int num_old_hapd = change->num_old_hapd; - size_t i, j, max_bss, same_bssid; - struct hostapd_bss_config *newbss, *oldbss; - u8 *prev_addr; - hostapd_iface_cb cb; - - free(change); - hapd_iface->change = NULL; - - if (status) { - printf("Failed to setup new interface config\n"); - - cb = hapd_iface->config_reload_cb; - hapd_iface->config_reload_cb = NULL; - - /* Invalid configuration - cleanup and terminate hostapd */ - hapd_iface->bss = old_hapd; - hapd_iface->num_bss = num_old_hapd; - hapd_iface->conf = hapd->iconf = oldconf; - hapd->conf = &oldconf->bss[0]; - hostapd_config_free(newconf); - free(new_hapd); - - cb(hapd_iface, -2); - - return; - } - - /* - * If any BSSes have been removed, added, or had their BSSIDs changed, - * completely remove and reinitialize such BSSes and all the BSSes - * following them since their BSSID might have changed. - */ - max_bss = oldconf->num_bss; - if (max_bss > newconf->num_bss) - max_bss = newconf->num_bss; - - for (i = 0; i < max_bss; i++) { - if (strcmp(oldconf->bss[i].iface, newconf->bss[i].iface) != 0 - || hostapd_mac_comp(oldconf->bss[i].bssid, - newconf->bss[i].bssid) != 0) - break; - } - same_bssid = i; - - for (i = 0; i < oldconf->num_bss; i++) { - oldbss = &oldconf->bss[i]; - newbss = NULL; - for (j = 0; j < newconf->num_bss; j++) { - if (strcmp(oldbss->iface, newconf->bss[j].iface) == 0) - { - newbss = &newconf->bss[j]; - break; - } - } - - if (newbss && i < same_bssid) { - hapd = hapd_iface->bss[j] = old_hapd[i]; - hapd->iconf = newconf; - hapd->conf = newbss; - hostapd_reconfig_bss(hapd, newbss, oldbss, oldconf, - beacon_changed); - } else { - hapd = old_hapd[i]; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Removing BSS (ifname %s)\n", - hapd->conf->iface); - hostapd_free_stas(hapd); - /* Send broadcast deauthentication for this BSS, but do - * not clear all STAs from the driver since other BSSes - * may have STA entries. The driver will remove all STA - * entries for this BSS anyway when the interface is - * being removed. */ -#if 0 - hostapd_deauth_all_stas(hapd); - hostapd_cleanup(hapd); -#endif - - free(hapd); - } - } - - - prev_addr = hapd_iface->bss[0]->own_addr; - hapd = hapd_iface->bss[0]; - for (j = 0; j < newconf->num_bss; j++) { - if (hapd_iface->bss[j] != NULL) { - prev_addr = hapd_iface->bss[j]->own_addr; - continue; - } - - newbss = &newconf->bss[j]; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Reconfiguration: adding " - "new BSS (ifname=%s)\n", newbss->iface); - -#if 0 - hapd = hapd_iface->bss[j] = - hostapd_alloc_bss_data(hapd_iface, newconf, newbss); -#endif - if (hapd == NULL) { - printf("Failed to initialize new BSS\n"); - /* FIX: This one is somewhat hard to recover - * from.. Would need to remove this BSS from - * conf and BSS list. */ - exit(1); - } - hapd->driver = hapd_iface->bss[0]->driver; - hapd->iface = hapd_iface; - hapd->iconf = newconf; - hapd->conf = newbss; - - memcpy(hapd->own_addr, prev_addr, ETH_ALEN); - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) - prev_addr = hapd->own_addr; - -#if 0 - if (hostapd_setup_bss(hapd, j == 0)) { - printf("Failed to setup new BSS\n"); - /* FIX */ - exit(1); - } -#endif - - } - - free(old_hapd); - hostapd_config_free(oldconf); - - cb = hapd_iface->config_reload_cb; - hapd_iface->config_reload_cb = NULL; - - cb(hapd_iface, 0); -} - - -/** - * hostapd_config_reload_start - Start reconfiguration of an interface - * @hapd_iface: Pointer to hostapd interface data - * @cb: Function to be called back when done. - * The status indicates: - * 0 = success, new configuration in use; - * -1 = failed to update configuraiton, old configuration in use; - * -2 = failed to update configuration and failed to recover; caller - * should cleanup and terminate hostapd - * Returns: - * 0 = reconfiguration started; - * -1 = failed to update configuration, old configuration in use; - * -2 = failed to update configuration and failed to recover; caller - * should cleanup and terminate hostapd - */ -int hostapd_config_reload_start(struct hostapd_iface *hapd_iface, - hostapd_iface_cb cb) -{ - struct hostapd_config *newconf, *oldconf; - struct hostapd_config_change *change; - struct hostapd_data *hapd = NULL; - struct hostapd_data **old_hapd, **new_hapd; - int num_old_hapd; - - if (hapd_iface->config_reload_cb) { - wpa_printf(MSG_DEBUG, "%s: Config reload already in progress.", - hapd_iface->bss[0]->conf->iface); - return -1; - } - - newconf = hostapd_config_read(hapd_iface->config_fname); - if (newconf == NULL) { - printf("Failed to read new configuration file - continuing " - "with old.\n"); - return -1; - } - - if (strcmp(newconf->bss[0].iface, hapd_iface->conf->bss[0].iface) != - 0) { - printf("Interface name changing is not allowed in " - "configuration reloading (%s -> %s).\n", - hapd_iface->conf->bss[0].iface, newconf->bss[0].iface); - hostapd_config_free(newconf); - return -1; - } - - new_hapd = wpa_zalloc(newconf->num_bss * - sizeof(struct hostapd_data *)); - if (new_hapd == NULL) { - hostapd_config_free(newconf); - return -1; - } - old_hapd = hapd_iface->bss; - num_old_hapd = hapd_iface->num_bss; - - hapd_iface->bss = new_hapd; - hapd_iface->num_bss = newconf->num_bss; - /* - * First BSS remains the same since interface name changing was - * prohibited above. Now, this is only used in - * hostapd_config_reload_iface() and following loop will anyway set - * this again. - */ - hapd = hapd_iface->bss[0] = old_hapd[0]; - - oldconf = hapd_iface->conf; - hapd->iconf = hapd_iface->conf = newconf; - hapd->conf = &newconf->bss[0]; - - change = wpa_zalloc(sizeof(struct hostapd_config_change)); - if (change == NULL) { - hostapd_config_free(newconf); - return -1; - } - - change->hapd = hapd; - change->newconf = newconf; - change->oldconf = oldconf; - change->beacon_changed = 0; - change->hapd_iface = hapd_iface; - change->new_hapd = new_hapd; - change->old_hapd = old_hapd; - change->num_old_hapd = num_old_hapd; - - hapd_iface->config_reload_cb = cb; - hapd_iface->change = change; - if (hostapd_config_reload_iface_start(hapd_iface, config_reload2)) { - printf("Failed to start setup of new interface config\n"); - - hapd_iface->config_reload_cb = NULL; - free(change); - hapd_iface->change = NULL; - - /* Invalid configuration - cleanup and terminate hostapd */ - hapd_iface->bss = old_hapd; - hapd_iface->num_bss = num_old_hapd; - hapd_iface->conf = hapd->iconf = oldconf; - hapd->conf = &oldconf->bss[0]; - hostapd_config_free(newconf); - free(new_hapd); - return -2; - } - - return 0; -} diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c deleted file mode 100644 index b92cb2fbac639..0000000000000 --- a/contrib/hostapd/wpa.c +++ /dev/null @@ -1,3781 +0,0 @@ -/* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS - -#include "hostapd.h" -#include "eapol_sm.h" -#include "wpa.h" -#include "wme.h" -#include "sha1.h" -#include "md5.h" -#include "rc4.h" -#include "aes_wrap.h" -#include "crypto.h" -#include "eloop.h" -#include "ieee802_11.h" -#include "pmksa_cache.h" -#include "state_machine.h" - -#define STATE_MACHINE_DATA struct wpa_state_machine -#define STATE_MACHINE_DEBUG_PREFIX "WPA" -#define STATE_MACHINE_ADDR sm->addr - - -#define RSN_NUM_REPLAY_COUNTERS_1 0 -#define RSN_NUM_REPLAY_COUNTERS_2 1 -#define RSN_NUM_REPLAY_COUNTERS_4 2 -#define RSN_NUM_REPLAY_COUNTERS_16 3 - - -struct wpa_group; - -struct wpa_stsl_negotiation { - struct wpa_stsl_negotiation *next; - u8 initiator[ETH_ALEN]; - u8 peer[ETH_ALEN]; -}; - - -struct wpa_state_machine { - struct wpa_authenticator *wpa_auth; - struct wpa_group *group; - - u8 addr[ETH_ALEN]; - - enum { - WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, - WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, - WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, - WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, - WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE - } wpa_ptk_state; - - enum { - WPA_PTK_GROUP_IDLE = 0, - WPA_PTK_GROUP_REKEYNEGOTIATING, - WPA_PTK_GROUP_REKEYESTABLISHED, - WPA_PTK_GROUP_KEYERROR - } wpa_ptk_group_state; - - Boolean Init; - Boolean DeauthenticationRequest; - Boolean AuthenticationRequest; - Boolean ReAuthenticationRequest; - Boolean Disconnect; - int TimeoutCtr; - int GTimeoutCtr; - Boolean TimeoutEvt; - Boolean EAPOLKeyReceived; - Boolean EAPOLKeyPairwise; - Boolean EAPOLKeyRequest; - Boolean MICVerified; - Boolean GUpdateStationKeys; - u8 ANonce[WPA_NONCE_LEN]; - u8 SNonce[WPA_NONCE_LEN]; - u8 PMK[WPA_PMK_LEN]; - struct wpa_ptk PTK; - Boolean PTK_valid; - Boolean pairwise_set; - int keycount; - Boolean Pair; - u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; - Boolean key_replay_counter_valid; - Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ - Boolean PTKRequest; /* not in IEEE 802.11i state machine */ - Boolean has_GTK; - - u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ - size_t last_rx_eapol_key_len; - - unsigned int changed:1; - unsigned int in_step_loop:1; - unsigned int pending_deinit:1; - unsigned int started:1; - unsigned int mgmt_frame_prot:1; - - u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; - int req_replay_counter_used; - - u8 *wpa_ie; - size_t wpa_ie_len; - - enum { - WPA_VERSION_NO_WPA = 0 /* WPA not used */, - WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, - WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ - } wpa; - int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ - struct rsn_pmksa_cache_entry *pmksa; - - u32 dot11RSNAStatsTKIPLocalMICFailures; - u32 dot11RSNAStatsTKIPRemoteMICFailures; -}; - - -/* per group key state machine data */ -struct wpa_group { - struct wpa_group *next; - int vlan_id; - - Boolean GInit; - int GKeyDoneStations; - Boolean GTKReKey; - int GTK_len; - int GN, GM; - Boolean GTKAuthenticator; - u8 Counter[WPA_NONCE_LEN]; - - enum { - WPA_GROUP_GTK_INIT = 0, - WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE - } wpa_group_state; - - u8 GMK[WPA_GMK_LEN]; - u8 GTK[2][WPA_GTK_MAX_LEN]; - u8 GNonce[WPA_NONCE_LEN]; - Boolean changed; -#ifdef CONFIG_IEEE80211W - u8 DGTK[WPA_DGTK_LEN]; - u8 IGTK[2][WPA_IGTK_LEN]; -#endif /* CONFIG_IEEE80211W */ -}; - - -/* per authenticator data */ -struct wpa_authenticator { - struct wpa_group *group; - - unsigned int dot11RSNAStatsTKIPRemoteMICFailures; - u8 dot11RSNAAuthenticationSuiteSelected[4]; - u8 dot11RSNAPairwiseCipherSelected[4]; - u8 dot11RSNAGroupCipherSelected[4]; - u8 dot11RSNAPMKIDUsed[PMKID_LEN]; - u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */ - u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */ - u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */ - unsigned int dot11RSNATKIPCounterMeasuresInvoked; - unsigned int dot11RSNA4WayHandshakeFailures; - - struct wpa_stsl_negotiation *stsl_negotiations; - - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; - - u8 *wpa_ie; - size_t wpa_ie_len; - - u8 addr[ETH_ALEN]; - - struct rsn_pmksa_cache *pmksa; -}; - - -static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); -static void wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); -static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); -static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, - struct wpa_group *group); -static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg); -static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, int key_info, - const u8 *key_rsc, const u8 *nonce, - const u8 *kde, size_t kde_len, - int keyidx, int encr, int force_version); - -/* Default timeouts are 100 ms, but this seems to be a bit too fast for most - * WPA Supplicants, so use a bit longer timeout. */ -static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */ -static const u32 dot11RSNAConfigGroupUpdateCount = 3; -static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */ -static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; - -/* TODO: make these configurable */ -static const int dot11RSNAConfigPMKLifetime = 43200; -static const int dot11RSNAConfigPMKReauthThreshold = 70; -static const int dot11RSNAConfigSATimeout = 60; - - -static const int WPA_SELECTOR_LEN = 4; -static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; -static const u16 WPA_VERSION = 1; -static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; -static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; -static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; -static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; -static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; -static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; -static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; -static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; -#ifdef CONFIG_IEEE80211W -static const u8 RSN_CIPHER_SUITE_AES_128_CMAC[] = { 0x00, 0x0f, 0xac, 6 }; -#endif /* CONFIG_IEEE80211W */ - -static const int RSN_SELECTOR_LEN = 4; -static const u16 RSN_VERSION = 1; -static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; -static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; -static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; -static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; -static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; -static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; -static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; -static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; - -/* EAPOL-Key Key Data Encapsulation - * GroupKey and PeerKey require encryption, otherwise, encryption is optional. - */ -static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; -#if 0 -static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; -#endif -static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; -static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; -#ifdef CONFIG_PEERKEY -static const u8 RSN_KEY_DATA_SMK[] = { 0x00, 0x0f, 0xac, 5 }; -static const u8 RSN_KEY_DATA_NONCE[] = { 0x00, 0x0f, 0xac, 6 }; -static const u8 RSN_KEY_DATA_LIFETIME[] = { 0x00, 0x0f, 0xac, 7 }; -static const u8 RSN_KEY_DATA_ERROR[] = { 0x00, 0x0f, 0xac, 8 }; -#endif /* CONFIG_PEERKEY */ -#ifdef CONFIG_IEEE80211W -/* FIX: IEEE 802.11w/D1.0 is using subtypes 5 and 6 for these, but they were - * already taken by 802.11ma (PeerKey). Need to update the values here once - * IEEE 802.11w fixes these. */ -static const u8 RSN_KEY_DATA_DHV[] = { 0x00, 0x0f, 0xac, 9 }; -static const u8 RSN_KEY_DATA_IGTK[] = { 0x00, 0x0f, 0xac, 10 }; -#endif /* CONFIG_IEEE80211W */ - -#ifdef CONFIG_PEERKEY -enum { - STK_MUI_4WAY_STA_AP = 1, - STK_MUI_4WAY_STAT_STA = 2, - STK_MUI_GTK = 3, - STK_MUI_SMK = 4 -}; - -enum { - STK_ERR_STA_NR = 1, - STK_ERR_STA_NRSN = 2, - STK_ERR_CPHR_NS = 3, - STK_ERR_NO_STSL = 4 -}; -#endif /* CONFIG_PEERKEY */ - -#define GENERIC_INFO_ELEM 0xdd -#define RSN_INFO_ELEM 0x30 - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -/* WPA IE version 1 - * 00-50-f2:1 (OUI:OUI type) - * 0x01 0x00 (version; little endian) - * (all following fields are optional:) - * Group Suite Selector (4 octets) (default: TKIP) - * Pairwise Suite Count (2 octets, little endian) (default: 1) - * Pairwise Suite List (4 * n octets) (default: TKIP) - * Authenticated Key Management Suite Count (2 octets, little endian) - * (default: 1) - * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1X) - * WPA Capabilities (2 octets, little endian) (default: 0) - */ - -struct wpa_ie_hdr { - u8 elem_id; - u8 len; - u8 oui[3]; - u8 oui_type; - u16 version; -} STRUCT_PACKED; - - -/* RSN IE version 1 - * 0x01 0x00 (version; little endian) - * (all following fields are optional:) - * Group Suite Selector (4 octets) (default: CCMP) - * Pairwise Suite Count (2 octets, little endian) (default: 1) - * Pairwise Suite List (4 * n octets) (default: CCMP) - * Authenticated Key Management Suite Count (2 octets, little endian) - * (default: 1) - * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1X) - * RSN Capabilities (2 octets, little endian) (default: 0) - * PMKID Count (2 octets) (default: 0) - * PMKID List (16 * n octets) - * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) - */ - -struct rsn_ie_hdr { - u8 elem_id; /* WLAN_EID_RSN */ - u8 len; - u16 version; -} STRUCT_PACKED; - - -struct rsn_error_kde { - u16 mui; - u16 error_type; -} STRUCT_PACKED; - - -#ifdef CONFIG_IEEE80211W -struct wpa_dhv_kde { - u8 dhv[WPA_DHV_LEN]; -} STRUCT_PACKED; - -struct wpa_igtk_kde { - u8 keyid[2]; - u8 pn[6]; - u8 igtk[WPA_IGTK_LEN]; -} STRUCT_PACKED; -#endif /* CONFIG_IEEE80211W */ - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ - - -static inline void wpa_auth_mic_failure_report( - struct wpa_authenticator *wpa_auth, const u8 *addr) -{ - if (wpa_auth->cb.mic_failure_report) - wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); -} - - -static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, - const u8 *addr, wpa_eapol_variable var, - int value) -{ - if (wpa_auth->cb.set_eapol) - wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); -} - - -static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, - const u8 *addr, wpa_eapol_variable var) -{ - if (wpa_auth->cb.get_eapol == NULL) - return -1; - return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); -} - - -static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, - const u8 *addr, const u8 *prev_psk) -{ - if (wpa_auth->cb.get_psk == NULL) - return NULL; - return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); -} - - -static inline int wpa_auth_get_pmk(struct wpa_authenticator *wpa_auth, - const u8 *addr, u8 *pmk, size_t *len) -{ - if (wpa_auth->cb.get_pmk == NULL) - return -1; - return wpa_auth->cb.get_pmk(wpa_auth->cb.ctx, addr, pmk, len); -} - - -static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, - int vlan_id, - const char *alg, const u8 *addr, int idx, - u8 *key, size_t key_len) -{ - if (wpa_auth->cb.set_key == NULL) - return -1; - return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, - key, key_len); -} - - -static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, - const u8 *addr, int idx, u8 *seq) -{ - if (wpa_auth->cb.get_seqnum == NULL) - return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); -} - - -static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, - const u8 *addr, int idx, u8 *seq) -{ - if (wpa_auth->cb.get_seqnum_igtk == NULL) - return -1; - return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); -} - - -static inline int -wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *data, size_t data_len, int encrypt) -{ - if (wpa_auth->cb.send_eapol == NULL) - return -1; - return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, - encrypt); -} - - -static inline int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, - int (*cb)(struct wpa_state_machine *sm, - void *ctx), - void *cb_ctx) -{ - if (wpa_auth->cb.for_each_sta == NULL) - return 0; - return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); -} - - -static void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, - logger_level level, const char *txt) -{ - if (wpa_auth->cb.logger == NULL) - return; - wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); -} - - -static void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, - const u8 *addr, logger_level level, - const char *fmt, ...) -{ - char *format; - int maxlen; - va_list ap; - - if (wpa_auth->cb.logger == NULL) - return; - - maxlen = strlen(fmt) + 100; - format = malloc(maxlen); - if (!format) - return; - - va_start(ap, fmt); - vsnprintf(format, maxlen, fmt, ap); - va_end(ap); - - wpa_auth_logger(wpa_auth, addr, level, format); - - free(format); -} - - -static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) -{ - struct wpa_ie_hdr *hdr; - int num_suites; - u8 *pos, *count; - - hdr = (struct wpa_ie_hdr *) buf; - hdr->elem_id = WLAN_EID_GENERIC; - memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); - hdr->version = host_to_le16(WPA_VERSION); - pos = (u8 *) (hdr + 1); - - if (conf->wpa_group == WPA_CIPHER_CCMP) { - memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); - } else { - wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", - conf->wpa_group); - return -1; - } - pos += WPA_SELECTOR_LEN; - - num_suites = 0; - count = pos; - pos += 2; - - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { - memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { - memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_NONE) { - memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", - conf->wpa_pairwise); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - num_suites = 0; - count = pos; - pos += 2; - - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, - WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", - conf->wpa_key_mgmt); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - /* WPA Capabilities; use defaults, so no need to include it */ - - hdr->len = (pos - buf) - 2; - - return pos - buf; -} - - -static int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) -{ - struct rsn_ie_hdr *hdr; - int num_suites; - u8 *pos, *count; - u16 capab; - - hdr = (struct rsn_ie_hdr *) buf; - hdr->elem_id = WLAN_EID_RSN; - pos = (u8 *) &hdr->version; - *pos++ = RSN_VERSION & 0xff; - *pos++ = RSN_VERSION >> 8; - pos = (u8 *) (hdr + 1); - - if (conf->wpa_group == WPA_CIPHER_CCMP) { - memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); - } else { - wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", - conf->wpa_group); - return -1; - } - pos += RSN_SELECTOR_LEN; - - num_suites = 0; - count = pos; - pos += 2; - - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { - memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { - memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_NONE) { - memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", - conf->wpa_pairwise); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - num_suites = 0; - count = pos; - pos += 2; - - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, - RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", - conf->wpa_key_mgmt); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - /* RSN Capabilities */ - capab = 0; - if (conf->rsn_preauth) - capab |= WPA_CAPABILITY_PREAUTH; - if (conf->peerkey) - capab |= WPA_CAPABILITY_PEERKEY_ENABLED; - if (conf->wme_enabled) { - /* 4 PTKSA replay counters when using WME */ - capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); - } -#ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != WPA_NO_IEEE80211W) - capab |= WPA_CAPABILITY_MGMT_FRAME_PROTECTION; -#endif /* CONFIG_IEEE80211W */ - *pos++ = capab & 0xff; - *pos++ = capab >> 8; - -#ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != WPA_NO_IEEE80211W) { - if (pos + 2 + 4 > buf + len) - return -1; - /* PMKID Count */ - WPA_PUT_LE16(pos, 0); - pos += 2; - - /* Management Group Cipher Suite */ - memcpy(pos, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - } -#endif /* CONFIG_IEEE80211W */ - - hdr->len = (pos - buf) - 2; - - return pos - buf; -} - - -static int wpa_gen_wpa_ie(struct wpa_authenticator *wpa_auth) -{ - u8 *pos, buf[100]; - int res; - - pos = buf; - - if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_write_rsn_ie(&wpa_auth->conf, - pos, buf + sizeof(buf) - pos); - if (res < 0) - return res; - pos += res; - } - if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA) { - res = wpa_write_wpa_ie(&wpa_auth->conf, - pos, buf + sizeof(buf) - pos); - if (res < 0) - return res; - pos += res; - } - - free(wpa_auth->wpa_ie); - wpa_auth->wpa_ie = malloc(pos - buf); - if (wpa_auth->wpa_ie == NULL) - return -1; - memcpy(wpa_auth->wpa_ie, buf, pos - buf); - wpa_auth->wpa_ie_len = pos - buf; - - return 0; -} - - -static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, - const u8 *addr) -{ - if (wpa_auth->cb.disconnect == NULL) - return; - wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); -} - - -static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_authenticator *wpa_auth = eloop_ctx; - - if (hostapd_get_rand(wpa_auth->group->GMK, WPA_GMK_LEN)) { - wpa_printf(MSG_ERROR, "Failed to get random data for WPA " - "initialization."); - } else { - wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); - } - - if (wpa_auth->conf.wpa_gmk_rekey) { - eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, - wpa_rekey_gmk, wpa_auth, NULL); - } -} - - -static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_group *group; - - wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); - for (group = wpa_auth->group; group; group = group->next) { - group->GTKReKey = TRUE; - do { - group->changed = FALSE; - wpa_group_sm_step(wpa_auth, group); - } while (group->changed); - } - - if (wpa_auth->conf.wpa_group_rekey) { - eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, - 0, wpa_rekey_gtk, wpa_auth, NULL); - } -} - - -static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) -{ - if (sm->pmksa == ctx) - sm->pmksa = NULL; - return 0; -} - - -static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, - void *ctx) -{ - struct wpa_authenticator *wpa_auth = ctx; - wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); -} - - -static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) -{ - struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; - - group = wpa_zalloc(sizeof(struct wpa_group)); - if (group == NULL) - return NULL; - - group->GTKAuthenticator = TRUE; - group->vlan_id = vlan_id; - - switch (wpa_auth->conf.wpa_group) { - case WPA_CIPHER_CCMP: - group->GTK_len = 16; - break; - case WPA_CIPHER_TKIP: - group->GTK_len = 32; - break; - case WPA_CIPHER_WEP104: - group->GTK_len = 13; - break; - case WPA_CIPHER_WEP40: - group->GTK_len = 5; - break; - } - - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) - */ - memcpy(buf, wpa_auth->addr, ETH_ALEN); - wpa_get_ntp_timestamp(buf + ETH_ALEN); - memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); - if (hostapd_get_rand(rkey, sizeof(rkey)) || - hostapd_get_rand(group->GMK, WPA_GMK_LEN)) { - wpa_printf(MSG_ERROR, "Failed to get random data for WPA " - "initialization."); - free(group); - return NULL; - } - - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - group->Counter, WPA_NONCE_LEN); - - group->GInit = TRUE; - wpa_group_sm_step(wpa_auth, group); - group->GInit = FALSE; - wpa_group_sm_step(wpa_auth, group); - - return group; -} - - -/** - * wpa_init - Initialize WPA authenticator - * @addr: Authenticator address - * @conf: Configuration for WPA authenticator - * Returns: Pointer to WPA authenticator data or %NULL on failure - */ -struct wpa_authenticator * wpa_init(const u8 *addr, - struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb) -{ - struct wpa_authenticator *wpa_auth; - - wpa_auth = wpa_zalloc(sizeof(struct wpa_authenticator)); - if (wpa_auth == NULL) - return NULL; - memcpy(wpa_auth->addr, addr, ETH_ALEN); - memcpy(&wpa_auth->conf, conf, sizeof(*conf)); - memcpy(&wpa_auth->cb, cb, sizeof(*cb)); - - if (wpa_gen_wpa_ie(wpa_auth)) { - wpa_printf(MSG_ERROR, "Could not generate WPA IE."); - free(wpa_auth); - return NULL; - } - - wpa_auth->group = wpa_group_init(wpa_auth, 0); - if (wpa_auth->group == NULL) { - free(wpa_auth->wpa_ie); - free(wpa_auth); - return NULL; - } - - wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); - if (wpa_auth->pmksa == NULL) { - wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); - free(wpa_auth->wpa_ie); - free(wpa_auth); - return NULL; - } - - if (wpa_auth->conf.wpa_gmk_rekey) { - eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, - wpa_rekey_gmk, wpa_auth, NULL); - } - - if (wpa_auth->conf.wpa_group_rekey) { - eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, - wpa_rekey_gtk, wpa_auth, NULL); - } - - return wpa_auth; -} - - -/** - * wpa_deinit - Deinitialize WPA authenticator - * @wpa_auth: Pointer to WPA authenticator data from wpa_init() - */ -void wpa_deinit(struct wpa_authenticator *wpa_auth) -{ - struct wpa_group *group, *prev; - - eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - - while (wpa_auth->stsl_negotiations) - wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); - - pmksa_cache_deinit(wpa_auth->pmksa); - - free(wpa_auth->wpa_ie); - - group = wpa_auth->group; - while (group) { - prev = group; - group = group->next; - free(prev); - } - - free(wpa_auth); -} - - -/** - * wpa_reconfig - Update WPA authenticator configuration - * @wpa_auth: Pointer to WPA authenticator data from wpa_init() - * @conf: Configuration for WPA authenticator - */ -int wpa_reconfig(struct wpa_authenticator *wpa_auth, - struct wpa_auth_config *conf) -{ - if (wpa_auth == NULL) - return 0; - - memcpy(&wpa_auth->conf, conf, sizeof(*conf)); - /* - * TODO: - * Disassociate stations if configuration changed - * Update WPA/RSN IE - */ - return 0; -} - - -static int wpa_selector_to_bitfield(u8 *s) -{ - if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_NONE; - if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP40; - if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_TKIP; - if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_CCMP; - if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(u8 *s) -{ - if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) - return WPA_KEY_MGMT_IEEE8021X; - if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == - 0) - return WPA_KEY_MGMT_PSK; - return 0; -} - - -static int rsn_selector_to_bitfield(u8 *s) -{ - if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_NONE; - if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP40; - if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_TKIP; - if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_CCMP; - if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP104; -#ifdef CONFIG_IEEE80211W - if (memcmp(s, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_AES_128_CMAC; -#endif /* CONFIG_IEEE80211W */ - return 0; -} - - -static int rsn_key_mgmt_to_bitfield(u8 *s) -{ - if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) - return WPA_KEY_MGMT_IEEE8021X; - if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == - 0) - return WPA_KEY_MGMT_PSK; - return 0; -} - - -static u8 * wpa_add_kde(u8 *pos, const u8 *kde, const u8 *data, - size_t data_len, const u8 *data2, size_t data2_len) -{ - *pos++ = GENERIC_INFO_ELEM; - *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; - memcpy(pos, kde, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - memcpy(pos, data, data_len); - pos += data_len; - if (data2) { - memcpy(pos, data2, data2_len); - pos += data2_len; - } - return pos; -} - - -struct wpa_ie_data { - int pairwise_cipher; - int group_cipher; - int key_mgmt; - int capabilities; - size_t num_pmkid; - u8 *pmkid; - int mgmt_group_cipher; -}; - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - struct wpa_ie_hdr *hdr; - u8 *pos; - int left; - int i, count; - - memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - data->mgmt_group_cipher = 0; - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) - return -1; - - hdr = (struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_GENERIC || - hdr->len != wpa_ie_len - 2 || - memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || - le_to_host16(hdr->version) != WPA_VERSION) { - return -2; - } - - pos = (u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - } - - if (left > 0) { - return -8; - } - - return 0; -} - - -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; - int left; - int i, count; - - memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_CCMP; - data->group_cipher = WPA_CIPHER_CCMP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; -#ifdef CONFIG_IEEE80211W - data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; -#else /* CONFIG_IEEE80211W */ - data->mgmt_group_cipher = 0; -#endif /* CONFIG_IEEE80211W */ - - if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) - return -1; - - hdr = (struct rsn_ie_hdr *) rsn_ie; - - if (hdr->elem_id != WLAN_EID_RSN || - hdr->len != rsn_ie_len - 2 || - le_to_host16(hdr->version) != RSN_VERSION) { - return -2; - } - - pos = (u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); - - if (left >= RSN_SELECTOR_LEN) { - data->group_cipher = rsn_selector_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= rsn_selector_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - } - - if (left >= 2) { - data->num_pmkid = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (left < (int) data->num_pmkid * PMKID_LEN) { - wpa_printf(MSG_DEBUG, "RSN: too short RSN IE for " - "PMKIDs (num=%lu, left=%d)", - (unsigned long) data->num_pmkid, left); - return -9; - } - data->pmkid = pos; - pos += data->num_pmkid * PMKID_LEN; - left -= data->num_pmkid * PMKID_LEN; - } - -#ifdef CONFIG_IEEE80211W - if (left >= 4) { - data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); - if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { - wpa_printf(MSG_DEBUG, "RSN: Unsupported management " - "group cipher 0x%x", - data->mgmt_group_cipher); - return -10; - } - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } -#endif /* CONFIG_IEEE80211W */ - - if (left > 0) { - return -8; - } - - return 0; -} - - -int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct wpa_ie_data data; - int ciphers, key_mgmt, res, version; - const u8 *selector; - size_t i; - - if (wpa_auth == NULL || sm == NULL) - return WPA_NOT_ENABLED; - - if (wpa_ie == NULL || wpa_ie_len < 1) - return WPA_INVALID_IE; - - if (wpa_ie[0] == WLAN_EID_RSN) - version = HOSTAPD_WPA_VERSION_WPA2; - else - version = HOSTAPD_WPA_VERSION_WPA; - - if (version == HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); - - selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; - if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) - selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; - else if (data.key_mgmt & WPA_KEY_MGMT_PSK) - selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, - selector, RSN_SELECTOR_LEN); - - selector = RSN_CIPHER_SUITE_CCMP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = RSN_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; - memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, - selector, RSN_SELECTOR_LEN); - - selector = RSN_CIPHER_SUITE_CCMP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = RSN_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; - memcpy(wpa_auth->dot11RSNAGroupCipherSelected, - selector, RSN_SELECTOR_LEN); - } else { - res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); - - selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; - if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) - selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; - else if (data.key_mgmt & WPA_KEY_MGMT_PSK) - selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, - selector, WPA_SELECTOR_LEN); - - selector = WPA_CIPHER_SUITE_TKIP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; - memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, - selector, WPA_SELECTOR_LEN); - - selector = WPA_CIPHER_SUITE_TKIP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; - memcpy(wpa_auth->dot11RSNAGroupCipherSelected, - selector, WPA_SELECTOR_LEN); - } - if (res) { - wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " - MACSTR " (res=%d)", MAC2STR(sm->addr), res); - wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); - return WPA_INVALID_IE; - } - - if (data.group_cipher != wpa_auth->conf.wpa_group) { - wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " - MACSTR, data.group_cipher, MAC2STR(sm->addr)); - return WPA_INVALID_GROUP; - } - - key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; - if (!key_mgmt) { - wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " - MACSTR, data.key_mgmt, MAC2STR(sm->addr)); - return WPA_INVALID_AKMP; - } - if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) - sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; - else - sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - - ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; - if (!ciphers) { - wpa_printf(MSG_DEBUG, "Invalid WPA pairwise cipher (0x%x) " - "from " MACSTR, - data.pairwise_cipher, MAC2STR(sm->addr)); - return WPA_INVALID_PAIRWISE; - } - -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { - if (!(data.capabilities & - WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) { - wpa_printf(MSG_DEBUG, "Management frame protection " - "required, but client did not enable it"); - return WPA_MGMT_FRAME_PROTECTION_VIOLATION; - } - - if (ciphers & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "Management frame protection " - "cannot use TKIP"); - return WPA_MGMT_FRAME_PROTECTION_VIOLATION; - } - - if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { - wpa_printf(MSG_DEBUG, "Unsupported management group " - "cipher %d", data.mgmt_group_cipher); - return WPA_INVALID_MGMT_GROUP_CIPHER; - } - } - - if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || - !(data.capabilities & WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) - sm->mgmt_frame_prot = 0; - else - sm->mgmt_frame_prot = 1; -#endif /* CONFIG_IEEE80211W */ - - if (ciphers & WPA_CIPHER_CCMP) - sm->pairwise = WPA_CIPHER_CCMP; - else - sm->pairwise = WPA_CIPHER_TKIP; - - /* TODO: clear WPA/WPA2 state if STA changes from one to another */ - if (wpa_ie[0] == WLAN_EID_RSN) - sm->wpa = WPA_VERSION_WPA2; - else - sm->wpa = WPA_VERSION_WPA; - - for (i = 0; i < data.num_pmkid; i++) { - wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", - &data.pmkid[i * PMKID_LEN], PMKID_LEN); - sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, - &data.pmkid[i * PMKID_LEN]); - if (sm->pmksa) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PMKID found from PMKSA cache " - "eap_type=%d vlan_id=%d", - sm->pmksa->eap_type_authsrv, - sm->pmksa->vlan_id); - memcpy(wpa_auth->dot11RSNAPMKIDUsed, - sm->pmksa->pmkid, PMKID_LEN); - break; - } - } - - if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { - free(sm->wpa_ie); - sm->wpa_ie = malloc(wpa_ie_len); - if (sm->wpa_ie == NULL) - return WPA_ALLOC_FAIL; - } - memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); - sm->wpa_ie_len = wpa_ie_len; - - return WPA_IE_OK; -} - - -struct wpa_eapol_ie_parse { - const u8 *wpa_ie; - size_t wpa_ie_len; - const u8 *rsn_ie; - size_t rsn_ie_len; - const u8 *pmkid; - const u8 *gtk; - size_t gtk_len; - const u8 *mac_addr; - size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ -}; - - -/** - * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs - * @pos: Pointer to the IE header - * @end: Pointer to the end of the Key Data buffer - * @ie: Pointer to parsed IE data - * Returns: 0 on success, 1 if end mark is found, -1 on failure - */ -static int wpa_parse_generic(const u8 *pos, const u8 *end, - struct wpa_eapol_ie_parse *ie) -{ - if (pos[1] == 0) - return 1; - - if (pos[1] >= 6 && - 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 (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 (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; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_MAC_ADDR, RSN_SELECTOR_LEN) == 0) { - ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; - ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_SMK, RSN_SELECTOR_LEN) == 0) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_NONCE, RSN_SELECTOR_LEN) == 0) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_LIFETIME, RSN_SELECTOR_LEN) == 0) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_ERROR, RSN_SELECTOR_LEN) == 0) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } -#endif /* CONFIG_PEERKEY */ - - return 0; -} - - -/** - * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs - * @buf: Pointer to the Key Data buffer - * @len: Key Data Length - * @ie: Pointer to parsed IE data - * Returns: 0 on success, -1 on failure - */ -static int wpa_parse_kde_ies(const u8 *buf, size_t len, - struct wpa_eapol_ie_parse *ie) -{ - const u8 *pos, *end; - int ret = 0; - - memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { - if (pos[0] == 0xdd && - ((pos == buf + len - 1) || pos[1] == 0)) { - /* Ignore padding */ - break; - } - if (pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " - "underflow (ie=%d len=%d)", pos[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_parse_generic(pos, end, ie); - if (ret < 0) - break; - if (ret > 0) { - ret = 0; - break; - } - } else { - wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " - "Key Data IE", pos, 2 + pos[1]); - } - } - - return ret; -} - - -struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) -{ - struct wpa_state_machine *sm; - - sm = wpa_zalloc(sizeof(struct wpa_state_machine)); - if (sm == NULL) - return NULL; - memcpy(sm->addr, addr, ETH_ALEN); - - sm->wpa_auth = wpa_auth; - sm->group = wpa_auth->group; - - return sm; -} - - -void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm) -{ - if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) - return; - - if (sm->started) { - memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); - sm->ReAuthenticationRequest = TRUE; - wpa_sm_step(sm); - return; - } - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "start authentication"); - sm->started = 1; - - sm->Init = TRUE; - wpa_sm_step(sm); - sm->Init = FALSE; - sm->AuthenticationRequest = TRUE; - wpa_sm_step(sm); -} - - -static void wpa_free_sta_sm(struct wpa_state_machine *sm) -{ - free(sm->last_rx_eapol_key); - free(sm->wpa_ie); - free(sm); -} - - -void wpa_auth_sta_deinit(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return; - - if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "strict rekeying - force GTK rekey since STA " - "is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, - NULL); - } - - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); - eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); - if (sm->in_step_loop) { - /* Must not free state machine while wpa_sm_step() is running. - * Freeing will be completed in the end of wpa_sm_step(). */ - wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " - "machine deinit for " MACSTR, MAC2STR(sm->addr)); - sm->pending_deinit = 1; - } else - wpa_free_sta_sm(sm); -} - - -static void wpa_request_new_ptk(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return; - - sm->PTKRequest = TRUE; - sm->PTK_valid = 0; -} - - -#ifdef CONFIG_PEERKEY -static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_stsl_negotiation *neg = timeout_ctx; -#endif - - /* TODO: ? */ -} - - -struct wpa_stsl_search { - const u8 *addr; - struct wpa_state_machine *sm; -}; - - -static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) -{ - struct wpa_stsl_search *search = ctx; - if (memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { - search->sm = sm; - return 1; - } - return 0; -} - - -static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, const u8 *peer, - u16 mui, u16 error_type) -{ - u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; - size_t kde_len; - u8 *pos; - struct rsn_error_kde error; - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK Error"); - - kde_len = 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde); - pos = kde; - - if (peer) { - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, - NULL, 0); - kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; - } - - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, - (u8 *) &error, sizeof(error), NULL, 0); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, - NULL, NULL, kde, kde_len, 0, 0, 0); -} - - -static void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 *buf, *pos; - size_t buf_len; - - if (wpa_parse_kde_ies((const u8 *) (key + 1), - ntohs(key->key_data_length), &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); - return; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M1"); - return; - } - - /* Initiator = sm->addr; Peer = kde.mac_addr */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - buf = malloc(buf_len); - if (buf == NULL) - return; - /* Initiator RSN IE */ - memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); - pos = buf + kde.rsn_ie_len; - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, - NULL, 0); - - /* SMK M2: - * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) - */ - - wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, - "Sending SMK M2"); - - __wpa_send_eapol(wpa_auth, search.sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, buf_len, 0, 0, 0); - - free(buf); - -} - - -static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M4: - * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, - * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, - * Lifetime KDE) - */ - - buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = malloc(buf_len); - if (buf == NULL) - return; - - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, - NULL, 0); - - /* Initiator Nonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, - NULL, 0); - - /* SMK with PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, - key->key_nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M4"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, buf_len, 0, 1, 0); - - free(buf); -} - - -static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk, const u8 *peer) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M5: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, - * Lifetime KDE)) - */ - - buf_len = kde->rsn_ie_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = malloc(buf_len); - if (buf == NULL) - return; - - /* Peer RSN IE */ - memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); - pos = buf + kde->rsn_ie_len; - - /* Peer MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); - - /* PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, - WPA_NONCE_LEN, NULL, 0); - - /* SMK and INonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, - kde->nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M5"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE, - NULL, kde->nonce, buf, buf_len, 0, 1, 0); - - free(buf); -} - - -static void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - - if (wpa_parse_kde_ies((const u8 *) (key + 1), - ntohs(key->key_data_length), &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); - return; - } - - if (kde.rsn_ie == NULL || - kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " - "Nonce KDE in SMK M3"); - return; - } - - /* Peer = sm->addr; Initiator = kde.mac_addr; - * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - if (hostapd_get_rand(smk, WPA_PMK_LEN)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); - return; - } - - /* SMK = PRF-256(Random number, "SMK Derivation", - * AA || Time || INonce || PNonce) - */ - memcpy(buf, wpa_auth->addr, ETH_ALEN); - pos = buf + ETH_ALEN; - wpa_get_ntp_timestamp(pos); - pos += 8; - memcpy(pos, kde.nonce, WPA_NONCE_LEN); - pos += WPA_NONCE_LEN; - memcpy(pos, key->key_nonce, WPA_NONCE_LEN); - sha1_prf(smk, WPA_PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, WPA_PMK_LEN); - - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, WPA_PMK_LEN); - - wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); - wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); - - /* Authenticator does not need SMK anymore and it is required to forget - * it. */ - memset(smk, 0, sizeof(*smk)); -} - - -static void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - struct rsn_error_kde error; - u16 mui, error_type; - - if (wpa_parse_kde_ies((const u8 *) (key + 1), - ntohs(key->key_data_length), &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " - "SMK Error"); - return; - } - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " - "associated for SMK Error message from " MACSTR, - MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); - return; - } - - memcpy(&error, kde.error, sizeof(error)); - mui = be_to_host16(error.mui); - error_type = be_to_host16(error.error_type); - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "STA reported SMK Error: Peer " MACSTR - " MUI %d Error Type %d", - MAC2STR(kde.mac_addr), mui, error_type); - - wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); -} -#endif /* CONFIG_PEERKEY */ - - -static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg) -{ -#ifdef CONFIG_PEERKEY - struct wpa_stsl_negotiation *pos, *prev; - - if (wpa_auth == NULL) - return -1; - pos = wpa_auth->stsl_negotiations; - prev = NULL; - while (pos) { - if (pos == neg) { - if (prev) - prev->next = pos->next; - else - wpa_auth->stsl_negotiations = pos->next; - - eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); - free(pos); - return 0; - } - prev = pos; - pos = pos->next; - } -#endif /* CONFIG_PEERKEY */ - - return -1; -} - - -void wpa_receive(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - u8 *data, size_t data_len) -{ - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, - SMK_M1, SMK_M3, SMK_ERROR } msg; - char *msgtxt; - struct wpa_eapol_ie_parse kde; - - if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) - return; - - if (data_len < sizeof(*hdr) + sizeof(*key)) - return; - - hdr = (struct ieee802_1x_hdr *) data; - key = (struct wpa_eapol_key *) (hdr + 1); - key_info = ntohs(key->key_info); - key_data_length = ntohs(key->key_data_length); - if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { - wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " - "key_data overflow (%d > %lu)", - key_data_length, - (unsigned long) (data_len - sizeof(*hdr) - - sizeof(*key))); - return; - } - - /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys - * are set */ - - if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == - (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { - if (key_info & WPA_KEY_INFO_ERROR) { - msg = SMK_ERROR; - msgtxt = "SMK Error"; - } else { - msg = SMK_M1; - msgtxt = "SMK M1"; - } - } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { - msg = SMK_M3; - msgtxt = "SMK M3"; - } else if (key_info & WPA_KEY_INFO_REQUEST) { - msg = REQUEST; - msgtxt = "Request"; - } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { - msg = GROUP_2; - msgtxt = "2/2 Group"; - } else if (key_data_length == 0) { - msg = PAIRWISE_4; - msgtxt = "4/4 Pairwise"; - } else { - msg = PAIRWISE_2; - msgtxt = "2/4 Pairwise"; - } - - if (key_info & WPA_KEY_INFO_REQUEST) { - if (sm->req_replay_counter_used && - memcmp(key->replay_counter, sm->req_replay_counter, - WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, - "received EAPOL-Key request with " - "replayed counter"); - return; - } - } - - if (!(key_info & WPA_KEY_INFO_REQUEST) && - (!sm->key_replay_counter_valid || - memcmp(key->replay_counter, sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN) != 0)) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key %s with unexpected " - "replay counter", msgtxt); - wpa_hexdump(MSG_DEBUG, "expected replay counter", - sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); - wpa_hexdump(MSG_DEBUG, "received replay counter", - key->replay_counter, WPA_REPLAY_COUNTER_LEN); - return; - } - - switch (msg) { - case PAIRWISE_2: - if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg 2/4 in " - "invalid state (%d) - dropped", - sm->wpa_ptk_state); - return; - } - if (sm->wpa_ie == NULL || - sm->wpa_ie_len != key_data_length || - memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "WPA IE from (Re)AssocReq did not " - "match with msg 2/4"); - if (sm->wpa_ie) { - wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", - sm->wpa_ie, sm->wpa_ie_len); - } - wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", - (u8 *) (key + 1), key_data_length); - /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(wpa_auth, sm->addr); - return; - } - break; - case PAIRWISE_4: - if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || - !sm->PTK_valid) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg 4/4 in " - "invalid state (%d) - dropped", - sm->wpa_ptk_state); - return; - } - break; - case GROUP_2: - if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING - || !sm->PTK_valid) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg 2/2 in " - "invalid state (%d) - dropped", - sm->wpa_ptk_group_state); - return; - } - break; -#ifdef CONFIG_PEERKEY - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - if (!wpa_auth->conf.peerkey) { - wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " - "PeerKey use disabled - ignoring message"); - return; - } - if (!sm->PTK_valid) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg SMK in " - "invalid state - dropped"); - return; - } - break; -#else /* CONFIG_PEERKEY */ - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - return; /* STSL disabled - ignore SMK messages */ -#endif /* CONFIG_PEERKEY */ - case REQUEST: - break; - } - - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "received EAPOL-Key frame (%s)", msgtxt); - - if (key_info & WPA_KEY_INFO_ACK) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received invalid EAPOL-Key: Key Ack set"); - return; - } - - if (!(key_info & WPA_KEY_INFO_MIC)) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received invalid EAPOL-Key: Key MIC not set"); - return; - } - - sm->MICVerified = FALSE; - if (sm->PTK_valid) { - if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key with invalid MIC"); - return; - } - sm->MICVerified = TRUE; - eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); - } - - if (key_info & WPA_KEY_INFO_REQUEST) { - if (sm->MICVerified) { - sm->req_replay_counter_used = 1; - memcpy(sm->req_replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } else { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key request with " - "invalid MIC"); - return; - } - - /* - * TODO: should decrypt key data field if encryption was used; - * even though MAC address KDE is not normally encrypted, - * supplicant is allowed to encrypt it. - */ - if (msg == SMK_ERROR) { -#ifdef CONFIG_PEERKEY - wpa_smk_error(wpa_auth, sm, key); -#endif /* CONFIG_PEERKEY */ - return; - } else if (key_info & WPA_KEY_INFO_ERROR) { - /* Supplicant reported a Michael MIC error */ - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - wpa_auth_mic_failure_report(wpa_auth, sm->addr); - sm->dot11RSNAStatsTKIPRemoteMICFailures++; - wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; - /* Error report is not a request for a new key - * handshake, but since Authenticator may do it, let's - * change the keys now anyway. */ - wpa_request_new_ptk(sm); - } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Request for new " - "4-Way Handshake"); - wpa_request_new_ptk(sm); -#ifdef CONFIG_PEERKEY - } else if (msg == SMK_M1) { - wpa_smk_m1(wpa_auth, sm, key); -#endif /* CONFIG_PEERKEY */ - } else if (key_data_length > 0 && - wpa_parse_kde_ies((const u8 *) (key + 1), - key_data_length, &kde) == 0 && - kde.mac_addr) { - } else { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Request for GTK " - "rekeying"); - /* FIX: why was this triggering PTK rekeying for the - * STA that requested Group Key rekeying?? */ - /* wpa_request_new_ptk(sta->wpa_sm); */ - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - wpa_rekey_gtk(wpa_auth, NULL); - } - } else { - /* Do not allow the same key replay counter to be reused. */ - sm->key_replay_counter_valid = FALSE; - } - -#ifdef CONFIG_PEERKEY - if (msg == SMK_M3) { - wpa_smk_m3(wpa_auth, sm, key); - return; - } -#endif /* CONFIG_PEERKEY */ - - free(sm->last_rx_eapol_key); - sm->last_rx_eapol_key = malloc(data_len); - if (sm->last_rx_eapol_key == NULL) - return; - memcpy(sm->last_rx_eapol_key, data, data_len); - sm->last_rx_eapol_key_len = data_len; - - sm->EAPOLKeyReceived = TRUE; - sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); - sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); - memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); - wpa_sm_step(sm); -} - - -static void wpa_pmk_to_ptk(const u8 *pmk, const u8 *addr1, const u8 *addr2, - const u8 *nonce1, const u8 *nonce2, - u8 *ptk, size_t ptk_len) -{ - u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; - - /* 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); - } else { - memcpy(data, addr2, ETH_ALEN); - memcpy(data + ETH_ALEN, addr1, ETH_ALEN); - } - - if (memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { - memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); - memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, - WPA_NONCE_LEN); - } else { - memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); - memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, - WPA_NONCE_LEN); - } - - sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", - data, sizeof(data), ptk, ptk_len); - - wpa_hexdump_key(MSG_DEBUG, "PMK", pmk, WPA_PMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "PTK", ptk, ptk_len); -} - - -static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, - u8 *gtk, size_t gtk_len) -{ - u8 data[ETH_ALEN + WPA_NONCE_LEN]; - - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ - memcpy(data, addr, ETH_ALEN); - memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); - - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); - - wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); -} - - -static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_state_machine *sm = timeout_ctx; - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); - sm->TimeoutEvt = TRUE; - wpa_sm_step(sm); -} - - -static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, - u8 *mic) -{ - u8 hash[SHA1_MAC_LEN]; - - switch (ver) { - case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: - hmac_md5(key, 16, data, len, mic); - break; - case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: - hmac_sha1(key, 16, data, len, hash); - memcpy(mic, hash, MD5_MAC_LEN); - break; - default: - return -1; - } - return 0; -} - - -static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, int key_info, - const u8 *key_rsc, const u8 *nonce, - const u8 *kde, size_t kde_len, - int keyidx, int encr, int force_version) -{ - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - size_t len; - int alg; - int key_data_len, pad_len = 0; - u8 *buf, *pos; - int version, pairwise; - - len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); - - if (force_version) - version = force_version; - else if (sm->pairwise == WPA_CIPHER_CCMP) - version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - - wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(secure=%d mic=%d ack=%d " - "install=%d pairwise=%d kde_len=%lu keyidx=%d encr=%d)", - (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, - (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, - (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, - (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, - pairwise, (unsigned long) kde_len, keyidx, encr); - - key_data_len = kde_len; - - if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && encr) { - pad_len = key_data_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - key_data_len += pad_len + 8; - } - - len += key_data_len; - - hdr = wpa_zalloc(len); - if (hdr == NULL) - return; - hdr->version = wpa_auth->conf.eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len - sizeof(*hdr)); - key = (struct wpa_eapol_key *) (hdr + 1); - - key->type = sm->wpa == WPA_VERSION_WPA2 ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - key_info |= version; - if (encr && sm->wpa == WPA_VERSION_WPA2) - key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; - if (sm->wpa != WPA_VERSION_WPA2) - key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; - key->key_info = htons(key_info); - - alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - switch (alg) { - case WPA_CIPHER_CCMP: - key->key_length = htons(16); - break; - case WPA_CIPHER_TKIP: - key->key_length = htons(32); - break; - case WPA_CIPHER_WEP40: - key->key_length = htons(5); - break; - case WPA_CIPHER_WEP104: - key->key_length = htons(13); - break; - } - if (key_info & WPA_KEY_INFO_SMK_MESSAGE) - key->key_length = htons(0); - - /* FIX: STSL: what to use as key_replay_counter? */ - inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); - memcpy(key->replay_counter, sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - sm->key_replay_counter_valid = TRUE; - - if (nonce) - memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); - - if (key_rsc) - memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); - - if (kde && !encr) { - memcpy(key + 1, kde, kde_len); - key->key_data_length = htons(kde_len); - } else if (encr && kde) { - buf = wpa_zalloc(key_data_len); - if (buf == NULL) { - free(hdr); - return; - } - pos = buf; - memcpy(pos, kde, kde_len); - pos += kde_len; - - if (pad_len) - *pos++ = 0xdd; - - wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", - buf, key_data_len); - if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf, - (u8 *) (key + 1)); - key->key_data_length = htons(key_data_len); - } else { - u8 ek[32]; - memcpy(key->key_iv, - sm->group->Counter + WPA_NONCE_LEN - 16, 16); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); - memcpy(ek, key->key_iv, 16); - memcpy(ek + 16, sm->PTK.encr_key, 16); - memcpy(key + 1, buf, key_data_len); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); - key->key_data_length = htons(key_data_len); - } - free(buf); - } - - if (key_info & WPA_KEY_INFO_MIC) { - if (!sm->PTK_valid) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PTK not valid when sending EAPOL-Key " - "frame"); - free(hdr); - return; - } - wpa_calc_eapol_key_mic(version, - sm->PTK.mic_key, (u8 *) hdr, len, - key->key_mic); - } - - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, - 1); - wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, - sm->pairwise_set); - free(hdr); -} - - -static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, int key_info, - const u8 *key_rsc, const u8 *nonce, - const u8 *kde, size_t kde_len, - int keyidx, int encr) -{ - int timeout_ms; - int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - - if (sm == NULL) - return; - - __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, - keyidx, encr, 0); - - timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : - dot11RSNAConfigGroupUpdateTimeOut; - eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, - wpa_send_eapol_timeout, wpa_auth, sm); -} - - -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) -{ - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - u16 key_info; - int ret = 0; - u8 mic[16]; - - if (data_len < sizeof(*hdr) + sizeof(*key)) - return -1; - - hdr = (struct ieee802_1x_hdr *) data; - key = (struct wpa_eapol_key *) (hdr + 1); - key_info = ntohs(key->key_info); - memcpy(mic, key->key_mic, 16); - memset(key->key_mic, 0, 16); - if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, - PTK->mic_key, data, data_len, key->key_mic) - || memcmp(mic, key->key_mic, 16) != 0) - ret = -1; - memcpy(key->key_mic, mic, 16); - return ret; -} - - -void wpa_remove_ptk(struct wpa_state_machine *sm) -{ - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); - sm->pairwise_set = FALSE; -} - - -void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) -{ - if (sm == NULL) - return; - - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "event %d notification", event); - - switch (event) { - case WPA_AUTH: - case WPA_ASSOC: - break; - case WPA_DEAUTH: - case WPA_DISASSOC: - sm->DeauthenticationRequest = TRUE; - break; - case WPA_REAUTH: - case WPA_REAUTH_EAPOL: - sm->ReAuthenticationRequest = TRUE; - break; - } - - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - - if (event != WPA_REAUTH_EAPOL) - wpa_remove_ptk(sm); - - wpa_sm_step(sm); -} - - -static const char * wpa_alg_txt(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return "CCMP"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return "WEP"; - default: - return ""; - } -} - - -SM_STATE(WPA_PTK, INITIALIZE) -{ - SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); - if (sm->Init) { - /* Init flag is not cleared here, so avoid busy - * loop by claiming nothing changed. */ - sm->changed = FALSE; - } - - sm->keycount = 0; - if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; - sm->GUpdateStationKeys = FALSE; - if (sm->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = FALSE; - if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and - * Local AA > Remote AA)) */) { - sm->Pair = TRUE; - } - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); - wpa_remove_ptk(sm); - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); - sm->TimeoutCtr = 0; - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_authorized, 0); - } -} - - -SM_STATE(WPA_PTK, DISCONNECT) -{ - SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); - sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->wpa_auth, sm->addr); -} - - -SM_STATE(WPA_PTK, DISCONNECTED) -{ - SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); - sm->DeauthenticationRequest = FALSE; -} - - -SM_STATE(WPA_PTK, AUTHENTICATION) -{ - SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); - memset(&sm->PTK, 0, sizeof(sm->PTK)); - sm->PTK_valid = FALSE; - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, - 1); - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); - sm->AuthenticationRequest = FALSE; -} - - -SM_STATE(WPA_PTK, AUTHENTICATION2) -{ - SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); - memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); - sm->ReAuthenticationRequest = FALSE; - /* IEEE 802.11i does not clear TimeoutCtr here, but this is more - * logical place than INITIALIZE since AUTHENTICATION2 can be - * re-entered on ReAuthenticationRequest without going through - * INITIALIZE. */ - sm->TimeoutCtr = 0; -} - - -SM_STATE(WPA_PTK, INITPMK) -{ - size_t len = WPA_PMK_LEN; - - SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); - if (sm->pmksa) { - wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - memcpy(sm->PMK, sm->pmksa->pmk, WPA_PMK_LEN); - } else if (wpa_auth_get_pmk(sm->wpa_auth, sm->addr, sm->PMK, &len) == - 0) { - wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " - "(len=%lu)", (unsigned long) len); - } else { - wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); - } - - sm->req_replay_counter_used = 0; - /* IEEE 802.11i does not set keyRun to FALSE, but not doing this - * will break reauthentication since EAPOL state machines may not be - * get into AUTHENTICATING state that clears keyRun before WPA state - * machine enters AUTHENTICATION2 state and goes immediately to INITPMK - * state and takes PMK from the previously used AAA Key. This will - * eventually fail in 4-Way Handshake because Supplicant uses PMK - * derived from the new AAA Key. Setting keyRun = FALSE here seems to - * be good workaround for this issue. */ - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); -} - - -SM_STATE(WPA_PTK, INITPSK) -{ - const u8 *psk; - SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); - if (psk) - memcpy(sm->PMK, psk, WPA_PMK_LEN); - sm->req_replay_counter_used = 0; -} - - -SM_STATE(WPA_PTK, PTKSTART) -{ - u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; - size_t pmkid_len = 0; - - SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); - sm->PTKRequest = FALSE; - sm->TimeoutEvt = FALSE; - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "sending 1/4 msg of 4-Way Handshake"); - /* - * TODO: Could add PMKID even with WPA2-PSK, but only if there is only - * one possible PSK for this STA. - */ - if (sm->wpa == WPA_VERSION_WPA2 && - sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) { - pmkid = buf; - pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; - pmkid[0] = WLAN_EID_GENERIC; - pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; - memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN); - if (sm->pmksa) - memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, - PMKID_LEN); - else { - /* - * Calculate PMKID since no PMKSA cache entry was - * available with pre-calculated PMKID. - */ - rsn_pmkid(sm->PMK, WPA_PMK_LEN, sm->wpa_auth->addr, - sm->addr, &pmkid[2 + RSN_SELECTOR_LEN]); - } - } - wpa_send_eapol(sm->wpa_auth, sm, - WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, - sm->ANonce, pmkid, pmkid_len, 0, 0); - sm->TimeoutCtr++; -} - - -SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) -{ - struct wpa_ptk PTK; - int ok = 0; - const u8 *pmk = NULL; - - SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); - sm->EAPOLKeyReceived = FALSE; - - /* WPA with IEEE 802.1X: use the derived PMK from EAP - * WPA-PSK: iterate through possible PSKs and select the one matching - * the packet */ - for (;;) { - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); - if (pmk == NULL) - break; - } else - pmk = sm->PMK; - - wpa_pmk_to_ptk(pmk, sm->wpa_auth->addr, sm->addr, - sm->ANonce, sm->SNonce, - (u8 *) &PTK, sizeof(PTK)); - - if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, - sm->last_rx_eapol_key_len) == 0) { - ok = 1; - break; - } - - if (sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) - break; - } - - if (!ok) { - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "invalid MIC in msg 2/4 of 4-Way Handshake"); - return; - } - - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); - - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - /* PSK may have changed from the previous choice, so update - * state machine data based on whatever PSK was selected here. - */ - memcpy(sm->PMK, pmk, WPA_PMK_LEN); - } - - sm->MICVerified = TRUE; - - memcpy(&sm->PTK, &PTK, sizeof(PTK)); - sm->PTK_valid = TRUE; -} - - -SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) -{ - SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); - sm->TimeoutCtr = 0; -} - - -#ifdef CONFIG_IEEE80211W - -static int ieee80211w_kde_len(struct wpa_state_machine *sm) -{ - if (sm->mgmt_frame_prot) { - return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_dhv_kde) + - 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); - } - - return 0; -} - - -static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) -{ - struct wpa_dhv_kde dhv; - struct wpa_igtk_kde igtk; - struct wpa_group *gsm = sm->group; - u8 mac[32]; - const u8 *addr[3]; - size_t len[3]; - - if (!sm->mgmt_frame_prot) - return pos; - - addr[0] = sm->wpa_auth->addr; - len[0] = ETH_ALEN; - addr[1] = sm->addr; - len[1] = ETH_ALEN; - addr[2] = gsm->DGTK; - len[2] = WPA_DGTK_LEN; - sha256_vector(3, addr, len, mac); - memcpy(dhv.dhv, mac, WPA_DHV_LEN); - wpa_hexdump_key(MSG_DEBUG, "WPA: DHV", dhv.dhv, WPA_DHV_LEN); - pos = wpa_add_kde(pos, RSN_KEY_DATA_DHV, - (const u8 *) &dhv, sizeof(dhv), NULL, 0); - - igtk.keyid[0] = gsm->GN; - igtk.keyid[1] = 0; - if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN, igtk.pn) < 0) - memset(igtk.pn, 0, sizeof(igtk.pn)); - memcpy(igtk.igtk, gsm->IGTK[gsm->GN - 1], WPA_IGTK_LEN); - pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, - (const u8 *) &igtk, sizeof(igtk), NULL, 0); - - return pos; -} - -#else /* CONFIG_IEEE80211W */ - -static int ieee80211w_kde_len(struct wpa_state_machine *sm) -{ - return 0; -} - - -static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) -{ - return pos; -} - -#endif /* CONFIG_IEEE80211W */ - - -SM_STATE(WPA_PTK, PTKINITNEGOTIATING) -{ - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; - size_t gtk_len, kde_len; - struct wpa_group *gsm = sm->group; - u8 *wpa_ie; - int wpa_ie_len, secure, keyidx, encr = 0; - - SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); - sm->TimeoutEvt = FALSE; - /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) - */ - memset(rsc, 0, WPA_KEY_RSC_LEN); - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); - wpa_ie = sm->wpa_auth->wpa_ie; - wpa_ie_len = sm->wpa_auth->wpa_ie_len; - if (sm->wpa == WPA_VERSION_WPA && - (sm->wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA2) && - wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { - /* WPA-only STA, remove RSN IE */ - wpa_ie = wpa_ie + wpa_ie[1] + 2; - wpa_ie_len = wpa_ie[1] + 2; - } - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "sending 3/4 msg of 4-Way Handshake"); - if (sm->wpa == WPA_VERSION_WPA2) { - /* WPA2 send GTK in the 4-way handshake */ - secure = 1; - gtk = gsm->GTK[gsm->GN - 1]; - gtk_len = gsm->GTK_len; - keyidx = gsm->GN; - _rsc = rsc; - encr = 1; - } else { - /* WPA does not include GTK in msg 3/4 */ - secure = 0; - gtk = NULL; - gtk_len = 0; - keyidx = 0; - _rsc = NULL; - } - - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); - if (gtk) - kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; - kde = malloc(kde_len); - if (kde == NULL) - return; - - pos = kde; - memcpy(pos, wpa_ie, wpa_ie_len); - pos += wpa_ie_len; - if (gtk) { - u8 hdr[2]; - hdr[0] = keyidx & 0x03; - hdr[1] = 0; - pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gtk, gtk_len); - } - pos = ieee80211w_kde_add(sm, pos); - - wpa_send_eapol(sm->wpa_auth, sm, - (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | - WPA_KEY_INFO_KEY_TYPE, - _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); - free(kde); - sm->TimeoutCtr++; -} - - -SM_STATE(WPA_PTK, PTKINITDONE) -{ - SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); - sm->EAPOLKeyReceived = FALSE; - if (sm->Pair) { - char *alg; - int klen; - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = "TKIP"; - klen = 32; - } else { - alg = "CCMP"; - klen = 16; - } - if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, - sm->PTK.tk1, klen)) { - wpa_sta_disconnect(sm->wpa_auth, sm->addr); - return; - } - /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ - sm->pairwise_set = TRUE; - - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_authorized, 1); - } - } - - if (0 /* IBSS == TRUE */) { - sm->keycount++; - if (sm->keycount == 2) { - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_portValid, 1); - } - } else { - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, - 1); - } - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); - wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); - if (sm->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = TRUE; - else - sm->has_GTK = TRUE; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, - "pairwise key handshake completed (%s)", - sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); -} - - -SM_STEP(WPA_PTK) -{ - struct wpa_authenticator *wpa_auth = sm->wpa_auth; - - if (sm->Init) - SM_ENTER(WPA_PTK, INITIALIZE); - else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) - SM_ENTER(WPA_PTK, DISCONNECT); - else if (sm->DeauthenticationRequest) - SM_ENTER(WPA_PTK, DISCONNECTED); - else if (sm->AuthenticationRequest) - SM_ENTER(WPA_PTK, AUTHENTICATION); - else if (sm->ReAuthenticationRequest) - SM_ENTER(WPA_PTK, AUTHENTICATION2); - else if (sm->PTKRequest) - SM_ENTER(WPA_PTK, PTKSTART); - else switch (sm->wpa_ptk_state) { - case WPA_PTK_INITIALIZE: - break; - case WPA_PTK_DISCONNECT: - SM_ENTER(WPA_PTK, DISCONNECTED); - break; - case WPA_PTK_DISCONNECTED: - SM_ENTER(WPA_PTK, INITIALIZE); - break; - case WPA_PTK_AUTHENTICATION: - SM_ENTER(WPA_PTK, AUTHENTICATION2); - break; - case WPA_PTK_AUTHENTICATION2: - if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && - wpa_auth_get_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_keyRun) > 0) - SM_ENTER(WPA_PTK, INITPMK); - else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - /* FIX: && 802.1X::keyRun */) - SM_ENTER(WPA_PTK, INITPSK); - break; - case WPA_PTK_INITPMK: - if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_keyAvailable) > 0) - SM_ENTER(WPA_PTK, PTKSTART); - else { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } - break; - case WPA_PTK_INITPSK: - if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) - SM_ENTER(WPA_PTK, PTKSTART); - else { - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, - "no PSK configured for the STA"); - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } - break; - case WPA_PTK_PTKSTART: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKSTART); - break; - case WPA_PTK_PTKCALCNEGOTIATING: - if (sm->MICVerified) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); - else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKSTART); - break; - case WPA_PTK_PTKCALCNEGOTIATING2: - SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); - break; - case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) - SM_ENTER(WPA_PTK, PTKINITDONE); - else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); - break; - case WPA_PTK_PTKINITDONE: - break; - } -} - - -SM_STATE(WPA_PTK_GROUP, IDLE) -{ - SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); - if (sm->Init) { - /* Init flag is not cleared here, so avoid busy - * loop by claiming nothing changed. */ - sm->changed = FALSE; - } - sm->GTimeoutCtr = 0; -} - - -SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) -{ - u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_group *gsm = sm->group; - u8 *kde, *pos, hdr[2]; - size_t kde_len; - - SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); - if (sm->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = FALSE; - sm->TimeoutEvt = FALSE; - /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ - memset(rsc, 0, WPA_KEY_RSC_LEN); - if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "sending 1/2 msg of Group Key Handshake"); - - if (sm->wpa == WPA_VERSION_WPA2) { - kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + - ieee80211w_kde_len(sm); - kde = malloc(kde_len); - if (kde == NULL) - return; - - pos = kde; - hdr[0] = gsm->GN & 0x03; - hdr[1] = 0; - pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gsm->GTK[gsm->GN - 1], gsm->GTK_len); - pos = ieee80211w_kde_add(sm, pos); - } else { - kde = gsm->GTK[gsm->GN - 1]; - pos = kde + gsm->GTK_len; - } - - wpa_send_eapol(sm->wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | - (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); - if (sm->wpa == WPA_VERSION_WPA2) - free(kde); - sm->GTimeoutCtr++; -} - - -SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) -{ - SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); - sm->EAPOLKeyReceived = FALSE; - if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; - sm->GUpdateStationKeys = FALSE; - sm->GTimeoutCtr = 0; - /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, - "group key handshake completed (%s)", - sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - sm->has_GTK = TRUE; -} - - -SM_STATE(WPA_PTK_GROUP, KEYERROR) -{ - SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); - if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; - sm->GUpdateStationKeys = FALSE; - sm->Disconnect = TRUE; -} - - -SM_STEP(WPA_PTK_GROUP) -{ - if (sm->Init) - SM_ENTER(WPA_PTK_GROUP, IDLE); - else switch (sm->wpa_ptk_group_state) { - case WPA_PTK_GROUP_IDLE: - if (sm->GUpdateStationKeys || - (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) - SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); - break; - case WPA_PTK_GROUP_REKEYNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - !sm->EAPOLKeyPairwise && sm->MICVerified) - SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); - else if (sm->GTimeoutCtr > - (int) dot11RSNAConfigGroupUpdateCount) - SM_ENTER(WPA_PTK_GROUP, KEYERROR); - else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); - break; - case WPA_PTK_GROUP_KEYERROR: - SM_ENTER(WPA_PTK_GROUP, IDLE); - break; - case WPA_PTK_GROUP_REKEYESTABLISHED: - SM_ENTER(WPA_PTK_GROUP, IDLE); - break; - } -} - - -static void wpa_gtk_update(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) -{ - /* FIX: is this the correct way of getting GNonce? */ - memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); - inc_byte_array(group->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, - group->GTK[group->GN - 1], group->GTK_len); - -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { - hostapd_get_rand(group->DGTK, WPA_DGTK_LEN); - wpa_hexdump_key(MSG_DEBUG, "DGTK", group->DGTK, WPA_DGTK_LEN); - hostapd_get_rand(group->IGTK[group->GN - 1], WPA_IGTK_LEN); - wpa_hexdump_key(MSG_DEBUG, "IGTK", - group->IGTK[group->GN - 1], WPA_IGTK_LEN); - } -#endif /* CONFIG_IEEE80211W */ -} - - -static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) -{ - wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " - "GTK_INIT (VLAN-ID %d)", group->vlan_id); - group->changed = FALSE; /* GInit is not cleared here; avoid loop */ - group->wpa_group_state = WPA_GROUP_GTK_INIT; - - /* GTK[0..N] = 0 */ - memset(group->GTK, 0, sizeof(group->GTK)); - group->GN = 1; - group->GM = 2; - /* GTK[GN] = CalcGTK() */ - wpa_gtk_update(wpa_auth, group); -} - - -static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) -{ - if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Not in PTKINITDONE; skip Group Key update"); - return 0; - } - sm->group->GKeyDoneStations++; - sm->GUpdateStationKeys = TRUE; - wpa_sm_step(sm); - return 0; -} - - -static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) -{ - int tmp; - - wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " - "SETKEYS (VLAN-ID %d)", group->vlan_id); - group->changed = TRUE; - group->wpa_group_state = WPA_GROUP_SETKEYS; - group->GTKReKey = FALSE; - tmp = group->GM; - group->GM = group->GN; - group->GN = tmp; - /* "GKeyDoneStations = GNoStations" is done in more robust way by - * counting the STAs that are marked with GUpdateStationKeys instead of - * including all STAs that could be in not-yet-completed state. */ - wpa_gtk_update(wpa_auth, group); - - wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); - wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", - group->GKeyDoneStations); -} - - -static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) -{ - wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " - "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); - group->changed = TRUE; - group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_txt(wpa_auth->conf.wpa_group), - NULL, group->GN, group->GTK[group->GN - 1], - group->GTK_len); - -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { - wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", - NULL, group->GN, group->IGTK[group->GN - 1], - WPA_IGTK_LEN); - wpa_auth_set_key(wpa_auth, group->vlan_id, "DGTK", - NULL, 0, group->DGTK, WPA_DGTK_LEN); - } -#endif /* CONFIG_IEEE80211W */ -} - - -static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) -{ - if (group->GInit) { - wpa_group_gtk_init(wpa_auth, group); - } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && - group->GTKAuthenticator) { - wpa_group_setkeysdone(wpa_auth, group); - } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && - group->GTKReKey) { - wpa_group_setkeys(wpa_auth, group); - } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { - if (group->GKeyDoneStations == 0) - wpa_group_setkeysdone(wpa_auth, group); - else if (group->GTKReKey) - wpa_group_setkeys(wpa_auth, group); - } -} - - -static void wpa_sm_step(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return; - - if (sm->in_step_loop) { - /* This should not happen, but if it does, make sure we do not - * end up freeing the state machine too early by exiting the - * recursive call. */ - wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); - return; - } - - sm->in_step_loop = 1; - do { - if (sm->pending_deinit) - break; - - sm->changed = FALSE; - sm->wpa_auth->group->changed = FALSE; - - SM_STEP_RUN(WPA_PTK); - if (sm->pending_deinit) - break; - SM_STEP_RUN(WPA_PTK_GROUP); - if (sm->pending_deinit) - break; - wpa_group_sm_step(sm->wpa_auth, sm->group); - } while (sm->changed || sm->wpa_auth->group->changed); - sm->in_step_loop = 0; - - if (sm->pending_deinit) { - wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " - "machine deinit for " MACSTR, MAC2STR(sm->addr)); - wpa_free_sta_sm(sm); - } -} - - -static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_state_machine *sm = eloop_ctx; - wpa_sm_step(sm); -} - - -void wpa_auth_sm_notify(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return; - eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); -} - - -void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) -{ - int tmp, i; - struct wpa_group *group; - - if (wpa_auth == NULL) - return; - - group = wpa_auth->group; - - for (i = 0; i < 2; i++) { - tmp = group->GM; - group->GM = group->GN; - group->GN = tmp; - wpa_gtk_update(wpa_auth, group); - } -} - - -static const char * wpa_bool_txt(int bool) -{ - return bool ? "TRUE" : "FALSE"; -} - - -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - -#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_authenticator *wpa_auth, char *buf, size_t buflen) -{ - int len = 0, ret; - char pmkid_txt[PMKID_LEN * 2 + 1]; - - if (wpa_auth == NULL) - return len; - - ret = snprintf(buf + len, buflen - len, - "dot11RSNAOptionImplemented=TRUE\n" -#ifdef CONFIG_RSN_PREAUTH - "dot11RSNAPreauthenticationImplemented=TRUE\n" -#else /* CONFIG_RSN_PREAUTH */ - "dot11RSNAPreauthenticationImplemented=FALSE\n" -#endif /* CONFIG_RSN_PREAUTH */ - "dot11RSNAEnabled=%s\n" - "dot11RSNAPreauthenticationEnabled=%s\n", - wpa_bool_txt(wpa_auth->conf.wpa & - HOSTAPD_WPA_VERSION_WPA2), - wpa_bool_txt(wpa_auth->conf.rsn_preauth)); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - - wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), - wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); - - ret = snprintf(buf + len, buflen - len, - "dot11RSNAConfigVersion=%u\n" - "dot11RSNAConfigPairwiseKeysSupported=9999\n" - /* FIX: dot11RSNAConfigGroupCipher */ - /* FIX: dot11RSNAConfigGroupRekeyMethod */ - /* FIX: dot11RSNAConfigGroupRekeyTime */ - /* FIX: dot11RSNAConfigGroupRekeyPackets */ - "dot11RSNAConfigGroupRekeyStrict=%u\n" - "dot11RSNAConfigGroupUpdateCount=%u\n" - "dot11RSNAConfigPairwiseUpdateCount=%u\n" - "dot11RSNAConfigGroupCipherSize=%u\n" - "dot11RSNAConfigPMKLifetime=%u\n" - "dot11RSNAConfigPMKReauthThreshold=%u\n" - "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" - "dot11RSNAConfigSATimeout=%u\n" - "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" - "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" - "dot11RSNAPMKIDUsed=%s\n" - "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" - "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" - "dot11RSNATKIPCounterMeasuresInvoked=%u\n" - "dot11RSNA4WayHandshakeFailures=%u\n" - "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", - RSN_VERSION, - !!wpa_auth->conf.wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(wpa_auth->conf.wpa_group), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(wpa_auth-> - dot11RSNAAuthenticationSuiteSelected), - RSN_SUITE_ARG(wpa_auth-> - dot11RSNAPairwiseCipherSelected), - RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), - pmkid_txt, - RSN_SUITE_ARG(wpa_auth-> - dot11RSNAAuthenticationSuiteRequested), - RSN_SUITE_ARG(wpa_auth-> - dot11RSNAPairwiseCipherRequested), - RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), - wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, - wpa_auth->dot11RSNA4WayHandshakeFailures); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - - /* TODO: dot11RSNAConfigPairwiseCiphersTable */ - /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ - - /* Private MIB */ - ret = snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", - wpa_auth->group->wpa_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - - return len; -} - - -int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) -{ - int len = 0, ret; - u8 not_used[4] = { 0, 0, 0, 0 }; - const u8 *pairwise = not_used; - - if (sm == NULL) - return 0; - - /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ - - /* dot11RSNAStatsEntry */ - - if (sm->wpa == WPA_VERSION_WPA) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sm->wpa == WPA_VERSION_WPA2) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = RSN_CIPHER_SUITE_NONE; - } else - return 0; - - ret = snprintf(buf + len, buflen - len, - /* TODO: dot11RSNAStatsIndex */ - "dot11RSNAStatsSTAAddress=" MACSTR "\n" - "dot11RSNAStatsVersion=1\n" - "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" - /* TODO: dot11RSNAStatsTKIPICVErrors */ - "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" - /* TODO: dot11RSNAStatsCCMPReplays */ - /* TODO: dot11RSNAStatsCCMPDecryptErrors */ - /* TODO: dot11RSNAStatsTKIPReplays */, - MAC2STR(sm->addr), - RSN_SUITE_ARG(pairwise), - sm->dot11RSNAStatsTKIPLocalMICFailures, - sm->dot11RSNAStatsTKIPRemoteMICFailures); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - - /* Private MIB */ - ret = snprintf(buf + len, buflen - len, - "hostapdWPAPTKState=%d\n" - "hostapdWPAPTKGroupState=%d\n", - sm->wpa_ptk_state, - sm->wpa_ptk_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - - return len; -} - - -void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) -{ - if (wpa_auth) - wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; -} - - -int wpa_auth_pairwise_set(struct wpa_state_machine *sm) -{ - return sm && sm->pairwise_set; -} - - -int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return -1; - return sm->wpa_key_mgmt; -} - - -int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) -{ - if (sm == NULL) - return 0; - return sm->wpa; -} - - -int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, - struct rsn_pmksa_cache_entry *entry) -{ - if (sm == NULL || sm->pmksa != entry) - return -1; - sm->pmksa = NULL; - return 0; -} - - -struct rsn_pmksa_cache_entry * -wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) -{ - return sm ? sm->pmksa : NULL; -} - - -void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) -{ - if (sm) - sm->dot11RSNAStatsTKIPLocalMICFailures++; -} - - -const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) -{ - if (wpa_auth == NULL) - return NULL; - *len = wpa_auth->wpa_ie_len; - return wpa_auth->wpa_ie; -} - - -int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, - int session_timeout, struct eapol_state_machine *eapol) -{ - if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) - return -1; - - if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, WPA_PMK_LEN, - sm->wpa_auth->addr, sm->addr, session_timeout, - eapol)) - return 0; - - return -1; -} - - -int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, - const u8 *pmk, size_t len, const u8 *sta_addr, - int session_timeout, - struct eapol_state_machine *eapol) -{ - if (wpa_auth == NULL) - return -1; - - if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, - sta_addr, session_timeout, eapol)) - return 0; - - return -1; -} - - -static struct wpa_group * -wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) -{ - struct wpa_group *group; - - if (wpa_auth == NULL || wpa_auth->group == NULL) - return NULL; - - wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", - vlan_id); - group = wpa_group_init(wpa_auth, vlan_id); - if (group == NULL) - return NULL; - - group->next = wpa_auth->group->next; - wpa_auth->group->next = group; - - return group; -} - - -int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) -{ - struct wpa_group *group; - - if (sm == NULL || sm->wpa_auth == NULL) - return 0; - - group = sm->wpa_auth->group; - while (group) { - if (group->vlan_id == vlan_id) - break; - group = group->next; - } - - if (group == NULL) { - group = wpa_auth_add_group(sm->wpa_auth, vlan_id); - if (group == NULL) - return -1; - } - - if (sm->group == group) - return 0; - - wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " - "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); - - sm->group = group; - return 0; -} - -#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/wpa.h b/contrib/hostapd/wpa.h deleted file mode 100644 index 633b2c56ab171..0000000000000 --- a/contrib/hostapd/wpa.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef WPA_H -#define WPA_H - -#include "wpa_common.h" - -#define WPA_PMK_LEN PMK_LEN -#define WPA_GMK_LEN 32 -#define WPA_GTK_MAX_LEN 32 -#define PMKID_LEN 16 - -#define WPA_CAPABILITY_PREAUTH BIT(0) -#define WPA_CAPABILITY_MGMT_FRAME_PROTECTION BIT(6) -#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) - -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[WPA_KEY_RSC_LEN]; - u8 key_id[8]; /* Reserved */ - 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) -#define WPA_KEY_INFO_SMK_MESSAGE BIT(13) - - -/* per STA state machine data */ - -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_authenticator; -struct wpa_state_machine; -struct rsn_pmksa_cache_entry; - - -struct wpa_auth_config { - int wpa; - int wpa_key_mgmt; - int wpa_pairwise; - int wpa_group; - int wpa_group_rekey; - int wpa_strict_rekey; - int wpa_gmk_rekey; - int rsn_preauth; - int eapol_version; - int peerkey; - int wme_enabled; -#ifdef CONFIG_IEEE80211W - enum { - WPA_NO_IEEE80211W = 0, - WPA_IEEE80211W_OPTIONAL = 1, - WPA_IEEE80211W_REQUIRED = 2 - } ieee80211w; -#endif /* CONFIG_IEEE80211W */ -}; - -typedef enum { - LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING -} logger_level; - -typedef enum { - WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, - WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, - WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx -} wpa_eapol_variable; - -struct wpa_auth_callbacks { - void *ctx; - void (*logger)(void *ctx, const u8 *addr, logger_level level, - const char *txt); - void (*disconnect)(void *ctx, const u8 *addr, u16 reason); - void (*mic_failure_report)(void *ctx, const u8 *addr); - void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, - int value); - int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); - const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); - int (*get_pmk)(void *ctx, const u8 *addr, u8 *pmk, size_t *len); - int (*set_key)(void *ctx, int vlan_id, const char *alg, const u8 *addr, - int idx, u8 *key, size_t key_len); - int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); - int (*get_seqnum_igtk)(void *ctx, const u8 *addr, int idx, u8 *seq); - int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, - size_t data_len, int encrypt); - int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, - void *ctx), void *cb_ctx); -}; - -struct wpa_authenticator * wpa_init(const u8 *addr, - struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb); -void wpa_deinit(struct wpa_authenticator *wpa_auth); -int wpa_reconfig(struct wpa_authenticator *wpa_auth, - struct wpa_auth_config *conf); - -enum { - WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, - WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, - WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER -}; - -int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *wpa_ie, size_t wpa_ie_len); -struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); -void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm); -void wpa_auth_sta_deinit(struct wpa_state_machine *sm); -void wpa_receive(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - u8 *data, size_t data_len); -typedef enum { - WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL -} wpa_event; -void wpa_remove_ptk(struct wpa_state_machine *sm); -void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); -void wpa_auth_sm_notify(struct wpa_state_machine *sm); -void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); -int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); -int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); -void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); -int wpa_auth_pairwise_set(struct wpa_state_machine *sm); -int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); -int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); -int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, - struct rsn_pmksa_cache_entry *entry); -struct rsn_pmksa_cache_entry * -wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); -void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); -const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, - size_t *len); -int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, - int session_timeout, struct eapol_state_machine *eapol); -int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, - const u8 *pmk, size_t len, const u8 *sta_addr, - int session_timeout, - struct eapol_state_machine *eapol); -int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); - -#endif /* WPA_H */ diff --git a/hostapd/.gitignore b/hostapd/.gitignore new file mode 100644 index 0000000000000..6dd2c2fd35c47 --- /dev/null +++ b/hostapd/.gitignore @@ -0,0 +1,7 @@ +*.d +.config +driver_conf.c +hostapd +hostapd_cli +hlr_auc_gw +nt_password_hash diff --git a/contrib/hostapd/ChangeLog b/hostapd/ChangeLog index ab28973f9bba4..46f085246c7ae 100644 --- a/contrib/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,38 +1,156 @@ ChangeLog for hostapd -2008-02-19 - v0.5.10 - * fixed EAP-SIM and EAP-AKA message parser to validate attribute - lengths properly to avoid potential crash caused by invalid messages +2009-02-15 - v0.6.8 + * increased hostapd_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G<seconds>) + * driver_nl80211: use Linux socket filter to improve performance + * added support for external Registrars with WPS (UPnP transport) + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (hostapd can now be configured to act as an integrated WPS Registrar + and provision credentials for WPS Enrollees using PIN and PBC + methods; external wireless Registrar can configure the AP, but + external WLAN Manager Registrars are not supported); WPS support can + be enabled by adding CONFIG_WPS=y into .config and setting the + runtime configuration variables in hostapd.conf (see WPS section in + the example configuration file); new hostapd_cli commands wps_pin and + wps_pbc are used to configure WPS negotiation; see README-WPS for + more details + * added IEEE 802.11n HT capability configuration (ht_capab) + * added support for generating Country IE based on nl80211 regulatory + information (added if ieee80211d=1 in configuration) + * fixed WEP authentication (both Open System and Shared Key) with + mac80211 + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * updated management frame protection to use IEEE 802.11w/D7.0 + * fixed retransmission of EAP requests if no response is received + +2008-11-23 - v0.6.6 + * added a new configuration option, wpa_ptk_rekey, that can be used to + enforce frequent PTK rekeying, e.g., to mitigate some attacks against + TKIP deficiencies + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * changed channel flags configuration to read the information from + the driver (e.g., via driver_nl80211 when using mac80211) instead of + using hostapd as the source of the regulatory information (i.e., + information from CRDA is now used with mac80211); this allows 5 GHz + channels to be used with hostapd (if allowed in the current + regulatory domain) + * fixed EAP-TLS message processing for the last TLS message if it is + large enough to require fragmentation (e.g., if a large Session + Ticket data is included) + * fixed listen interval configuration for nl80211 drivers + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * fixed EAP-FAST PAC-Opaque padding (0.6.4 broke this for some peer + identity lengths) + * fixed internal TLSv1 implementation for abbreviated handshake (used + by EAP-FAST server) + * added support for setting VLAN ID for STAs based on local MAC ACL + (accept_mac_file) as an alternative for RADIUS server-based + configuration + * updated management frame protection to use IEEE 802.11w/D6.0 + (adds a new association ping to protect against unauthenticated + authenticate or (re)associate request frames dropping association) + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * added new "driver wrapper" for RADIUS-only configuration + (driver=none in hostapd.conf; CONFIG_DRIVER_NONE=y in .config) + * fixed WPA/RSN IE validation to verify that the proto (WPA vs. WPA2) + is enabled in configuration + * changed EAP-FAST configuration to use separate fields for A-ID and + A-ID-Info (eap_fast_a_id_info) to allow A-ID to be set to a fixed + 16-octet len binary value for better interoperability with some peer + implementations; eap_fast_a_id is now configured as a hex string + * driver_nl80211: Updated to match the current Linux mac80211 AP mode + configuration (wireless-testing.git and Linux kernel releases + starting from 2.6.29) + +2008-08-10 - v0.6.4 + * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2 + Identity Request if identity is already known + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS/EAP-FAST + changes needed to run two methods in sequence (IF-T) and the IF-IMV + and IF-TNCCS interfaces from TNCS) + * added support for optional cryptobinding with PEAPv0 + * added fragmentation support for EAP-TNC + * added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled) + data + * added support for opportunistic key caching (OKC) + +2008-02-22 - v0.6.3 * fixed Reassociation Response callback processing when using internal - MLME (driver_{hostap,devicescape,test}.c) + MLME (driver_{hostap,nl80211,test}.c) + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * copy optional Proxy-State attributes into RADIUS response when acting + as a RADIUS authentication server + * fixed EAPOL state machine to handle a case in which no response is + received from the RADIUS authentication server; previous version + could have triggered a crash in some cases after a timeout * fixed EAP-SIM/AKA realm processing to allow decorated usernames to be used * added a workaround for EAP-SIM/AKA peers that include incorrect null termination in the username + * fixed EAP-SIM/AKA protected result indication to include AT_COUNTER + attribute in notification messages only when using fast + reauthentication * fixed EAP-SIM Start response processing for fast reauthentication case - * copy optional Proxy-State attributes into RADIUS response when acting - as a RADIUS authentication server + * added support for pending EAP processing in EAP-{PEAP,TTLS,FAST} + phase 2 to allow EAP-SIM and EAP-AKA to be used as the Phase 2 method -2007-12-02 - v0.5.9 +2008-01-01 - v0.6.2 + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect hostapd usage, but many of the APIs changed + and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (eap_sim_aka_result_ind=1) + * added support for configuring EAP-TTLS phase 2 non-EAP methods in + EAP server configuration; previously all four were enabled for every + phase 2 user, now all four are disabled by default and need to be + enabled with new method names TTLS-PAP, TTLS-CHAP, TTLS-MSCHAP, + TTLS-MSCHAPV2 + * removed old debug printing mechanism and the related 'debug' + parameter in the configuration file; debug verbosity is now set with + -d (or -dd) command line arguments + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added experimental, integrated TLSv1 server implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + * added support for EAP-FAST server method to the integrated EAP + server * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest draft (draft-ietf-emu-eap-gpsk-07.txt) - * fixed debugging code not to use potentially unaligned read to fetch - IPv4 addresses - -2007-05-28 - v0.5.8 - * updated driver_devicescape.c to build with the current - wireless-dev.git tree and net/d80211 changes - * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest - draft (draft-ietf-emu-eap-gpsk-03.txt) - * fixed EAP-MSCHAPv2 server to use a space between S and M parameters - in Success Request [Bug 203] - * added support for sending EAP-AKA Notifications in error cases - * RADIUS server: added support for processing duplicate messages - (retransmissions from RADIUS client) by replying with the previous - reply - -2006-12-31 - v0.5.7 + * added a new configuration parameter, rsn_pairwise, to allow different + pairwise cipher suites to be enabled for WPA and RSN/WPA2 + (note: if wpa_pairwise differs from rsn_pairwise, the driver will + either need to support this or will have to use the WPA/RSN IEs from + hostapd; currently, the included madwifi and bsd driver interfaces do + not have support for this) + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + +2007-05-28 - v0.6.0 + * added experimental IEEE 802.11r/D6.0 support * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 * updated EAP-PSK to use the IANA-allocated EAP type 47 * fixed EAP-PSK bit ordering of the Flags field @@ -40,6 +158,22 @@ ChangeLog for hostapd by reading wpa_psk_file [Bug 181] * fixed EAP-TTLS AVP parser processing for too short AVP lengths * fixed IPv6 connection to RADIUS accounting server + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * hlr_auc_gw: read GSM triplet file into memory and rotate through the + entries instead of only using the same three triplets every time + (this does not work properly with tests using multiple clients, but + provides bit better triplet data for testing a single client; anyway, + if a better quality triplets are needed, GSM-Milenage should be used + instead of hardcoded triplet file) + * fixed EAP-MSCHAPv2 server to use a space between S and M parameters + in Success Request [Bug 203] + * added support for sending EAP-AKA Notifications in error cases + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * RADIUS server: added support for processing duplicate messages + (retransmissions from RADIUS client) by replying with the previous + reply 2006-11-24 - v0.5.6 * added support for configuring and controlling multiple BSSes per diff --git a/hostapd/Makefile b/hostapd/Makefile new file mode 100644 index 0000000000000..7737e23449165 --- /dev/null +++ b/hostapd/Makefile @@ -0,0 +1,602 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to +# a file (undefine it, if you want to save in binary size) +CFLAGS += -DHOSTAPD_DUMP_STATE + +CFLAGS += -I../src +CFLAGS += -I../src/crypto +CFLAGS += -I../src/utils +CFLAGS += -I../src/common + +# Uncomment following line and set the path to your kernel tree include +# directory if your C library does not include all header files. +# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include + +-include .config + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 +endif + +OBJS = hostapd.o ieee802_1x.o eapol_sm.o \ + ieee802_11.o config.o ieee802_11_auth.o accounting.o \ + sta_info.o wpa.o ctrl_iface.o \ + drivers.o preauth.o pmksa_cache.o beacon.o \ + hw_features.o wme.o ap_list.o \ + mlme.o vlan_init.o wpa_auth_ie.o + +OBJS += ../src/utils/eloop.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS += ../src/utils/ip_addr.o + +OBJS += ../src/common/ieee802_11_common.o +OBJS += ../src/common/wpa_common.o + +OBJS += ../src/radius/radius.o +OBJS += ../src/radius/radius_client.o + +OBJS += ../src/crypto/md5.o +OBJS += ../src/crypto/rc4.o +OBJS += ../src/crypto/md4.o +OBJS += ../src/crypto/sha1.o +OBJS += ../src/crypto/des.o +OBJS += ../src/crypto/aes_wrap.o +OBJS += ../src/crypto/aes.o + +HOBJS=../src/hlr_auc_gw/hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/hlr_auc_gw/milenage.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o + +CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX + +ifdef CONFIG_IAPP +CFLAGS += -DCONFIG_IAPP +OBJS += iapp.o +endif + +ifdef CONFIG_RSN_PREAUTH +CFLAGS += -DCONFIG_RSN_PREAUTH +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +OBJS += peerkey.o +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += wpa_ft.o +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211N +CFLAGS += -DCONFIG_IEEE80211N +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS += driver_hostap.o +endif + +ifdef CONFIG_DRIVER_WIRED +CFLAGS += -DCONFIG_DRIVER_WIRED +OBJS += driver_wired.o +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS += driver_madwifi.o +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS += driver_prism54.o +endif + +ifdef CONFIG_DRIVER_NL80211 +CFLAGS += -DCONFIG_DRIVER_NL80211 +OBJS += driver_nl80211.o radiotap.o +LIBS += -lnl +ifdef CONFIG_LIBNL20 +LIBS += -lnl-genl +CFLAGS += -DCONFIG_LIBNL20 +endif +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS += driver_bsd.o +CONFIG_L2_PACKET=y +CONFIG_DNET_PCAP=y +CONFIG_L2_FREEBSD=y +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS += driver_test.o +endif + +ifdef CONFIG_DRIVER_NONE +CFLAGS += -DCONFIG_DRIVER_NONE +OBJS += driver_none.o +endif + +ifdef CONFIG_L2_PACKET +ifdef CONFIG_DNET_PCAP +ifdef CONFIG_L2_FREEBSD +LIBS += -lpcap +OBJS += ../src/l2_packet/l2_packet_freebsd.o +else +LIBS += -ldnet -lpcap +OBJS += ../src/l2_packet/l2_packet_pcap.o +endif +else +OBJS += ../src/l2_packet/l2_packet_linux.o +endif +else +OBJS += ../src/l2_packet/l2_packet_none.o +endif + + +ifdef CONFIG_EAP_MD5 +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_server/eap_md5.o +CHAP=y +endif + +ifdef CONFIG_EAP_TLS +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_server/eap_tls.o +TLS_FUNCS=y +endif + +ifdef CONFIG_EAP_PEAP +CFLAGS += -DEAP_PEAP +OBJS += ../src/eap_server/eap_peap.o +OBJS += ../src/eap_common/eap_peap_common.o +TLS_FUNCS=y +CONFIG_EAP_MSCHAPV2=y +endif + +ifdef CONFIG_EAP_TTLS +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_server/eap_ttls.o +TLS_FUNCS=y +CHAP=y +endif + +ifdef CONFIG_EAP_MSCHAPV2 +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_server/eap_mschapv2.o +MS_FUNCS=y +endif + +ifdef CONFIG_EAP_GTC +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_server/eap_gtc.o +endif + +ifdef CONFIG_EAP_SIM +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_server/eap_sim.o +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_AKA +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_server/eap_aka.o +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +CFLAGS += -DEAP_AKA_PRIME +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += ../src/eap_common/eap_sim_common.o +# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be +# replaced with another file implementating the interface specified in +# eap_sim_db.h. +OBJS += ../src/eap_server/eap_sim_db.o +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_PAX +CFLAGS += -DEAP_PAX +OBJS += ../src/eap_server/eap_pax.o ../src/eap_common/eap_pax_common.o +endif + +ifdef CONFIG_EAP_PSK +CFLAGS += -DEAP_PSK +OBJS += ../src/eap_server/eap_psk.o ../src/eap_common/eap_psk_common.o +endif + +ifdef CONFIG_EAP_SAKE +CFLAGS += -DEAP_SAKE +OBJS += ../src/eap_server/eap_sake.o ../src/eap_common/eap_sake_common.o +endif + +ifdef CONFIG_EAP_GPSK +CFLAGS += -DEAP_GPSK +OBJS += ../src/eap_server/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +CFLAGS += -DEAP_VENDOR_TEST +OBJS += ../src/eap_server/eap_vendor_test.o +endif + +ifdef CONFIG_EAP_FAST +CFLAGS += -DEAP_FAST +OBJS += ../src/eap_server/eap_fast.o +OBJS += ../src/eap_common/eap_fast_common.o +TLS_FUNCS=y +NEED_T_PRF=y +endif + +ifdef CONFIG_WPS +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += ../src/utils/uuid.o +OBJS += wps_hostapd.o +OBJS += ../src/eap_server/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +OBJS += ../src/wps/wps_attr_process.o +OBJS += ../src/wps/wps_dev_attr.o +OBJS += ../src/wps/wps_enrollee.o +OBJS += ../src/wps/wps_registrar.o +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_CRYPTO=y +NEED_BASE64=y + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/httpread.o +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +CFLAGS += -DEAP_IKEV2 +OBJS += ../src/eap_server/eap_ikev2.o ../src/eap_server/ikev2.o +OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_EAP_TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_server/eap_tnc.o +OBJS += ../src/eap_server/tncs.o +NEED_BASE64=y +LIBS += -ldl +endif + +# Basic EAP functionality is needed for EAPOL +OBJS += ../src/eap_server/eap.o +OBJS += ../src/eap_common/eap_common.o +OBJS += ../src/eap_server/eap_methods.o +OBJS += ../src/eap_server/eap_identity.o + +ifdef CONFIG_EAP +CFLAGS += -DEAP_SERVER +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif +ifeq ($(CONFIG_CRYPTO), internal) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif + + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += ../src/eap_server/eap_tls_common.o +NEED_TLS_PRF=y +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +LIBS_h += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgcrypt -lgpg-error +LIBS_p += -lgcrypt +LIBS_h += -lgcrypt +endif +ifdef CONFIG_GNUTLS_EXTRA +CFLAGS += -DCONFIG_GNUTLS_EXTRA +LIBS += -lgnutls-extra +endif +ifeq ($(CONFIG_TLS), internal) +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o ../src/tls/tlsv1_server.o +OBJS += ../src/tls/tlsv1_server_write.o ../src/tls/tlsv1_server_read.o +OBJS += ../src/tls/asn1.o ../src/tls/x509v3.o +OBJS_p += ../src/tls/asn1.o +OBJS_p += ../src/crypto/rc4.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o +NEED_BASE64=y +CFLAGS += -DCONFIG_TLS_INTERNAL +CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER +ifeq ($(CONFIG_CRYPTO), internal) +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +NEED_CRYPTO=y +else +OBJS += ../src/crypto/tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_CRYPTO=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +endif + +ifdef NEED_CRYPTO +ifndef TLS_FUNCS +ifeq ($(CONFIG_TLS), openssl) +LIBS += -lcrypto +LIBS_p += -lcrypto +LIBS_h += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +LIBS += -lgcrypt +LIBS_p += -lgcrypt +LIBS_h += -lgcrypt +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +endif +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +HOBJS += ../src/crypto/crypto_openssl.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/crypto_gnutls.o +OBJS_p += ../src/crypto/crypto_gnutls.o +HOBJS += ../src/crypto/crypto_gnutls.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +OBJS_p += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif +endif +else +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif + +ifdef CONFIG_INTERNAL_AES +CFLAGS += -DINTERNAL_AES +endif +ifdef CONFIG_INTERNAL_SHA1 +CFLAGS += -DINTERNAL_SHA1 +endif +ifdef CONFIG_INTERNAL_SHA256 +CFLAGS += -DINTERNAL_SHA256 +endif +ifdef CONFIG_INTERNAL_MD5 +CFLAGS += -DINTERNAL_MD5 +endif +ifdef CONFIG_INTERNAL_MD4 +CFLAGS += -DINTERNAL_MD4 +endif +ifdef CONFIG_INTERNAL_DES +CFLAGS += -DINTERNAL_DES +endif + +ifdef NEED_SHA256 +OBJS += ../src/crypto/sha256.o +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +endif + +ifndef NEED_FIPS186_2_PRF +CFLAGS += -DCONFIG_NO_FIPS186_2_PRF +endif + +ifndef NEED_T_PRF +CFLAGS += -DCONFIG_NO_T_PRF +endif + +ifndef NEED_TLS_PRF +CFLAGS += -DCONFIG_NO_TLS_PRF +endif + +ifdef CONFIG_RADIUS_SERVER +CFLAGS += -DRADIUS_SERVER +OBJS += ../src/radius/radius_server.o +endif + +ifdef CONFIG_IPV6 +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef CONFIG_DRIVER_RADIUS_ACL +CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL +endif + +ifdef CONFIG_FULL_DYNAMIC_VLAN +# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges +# and vlan interfaces for the vlan feature. +CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +endif + +ifdef CONFIG_NO_AES_EXTRAS +CFLAGS += -DCONFIG_NO_AES_UNWRAP +CFLAGS += -DCONFIG_NO_AES_CTR -DCONFIG_NO_AES_OMAC1 +CFLAGS += -DCONFIG_NO_AES_EAX -DCONFIG_NO_AES_CBC +CFLAGS += -DCONFIG_NO_AES_DECRYPT +CFLAGS += -DCONFIG_NO_AES_ENCRYPT_BLOCK +endif + +ALL=hostapd hostapd_cli + +all: verify_config $(ALL) + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building hostapd requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +install: all + for i in $(ALL); do cp $$i /usr/local/bin/$$i; done + +hostapd: $(OBJS) + $(CC) -o hostapd $(OBJS) $(LIBS) + +OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o +hostapd_cli: $(OBJS_c) + $(CC) -o hostapd_cli $(OBJS_c) + +NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o ../src/crypto/sha1.o ../src/crypto/rc4.o ../src/crypto/md5.o +NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o +ifdef TLS_FUNCS +LIBS_n += -lcrypto +endif + +nt_password_hash: $(NOBJS) + $(CC) -o nt_password_hash $(NOBJS) $(LIBS_n) + +hlr_auc_gw: $(HOBJS) + $(CC) -o hlr_auc_gw $(HOBJS) $(LIBS_h) + +clean: + $(MAKE) -C ../src clean + rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw + rm -f *.d + +%.eps: %.fig + fig2dev -L eps $*.fig $*.eps + +%.png: %.fig + fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \ + > $*.png + +docs-pics: doc/hostapd.png doc/hostapd.eps + +docs: docs-pics + (cd ..; doxygen hostapd/doc/doxygen.full; cd hostapd) + $(MAKE) -C doc/latex + cp doc/latex/refman.pdf hostapd-devel.pdf + +docs-fast: docs-pics + (cd ..; doxygen hostapd/doc/doxygen.fast; cd hostapd) + +clean-docs: + rm -rf doc/latex doc/html + rm -f doc/hostapd.{eps,png} hostapd-devel.pdf + +TEST_SRC_MILENAGE = ../src/hlr_auc_gw/milenage.c ../src/crypto/aes_wrap.c ../src/crypto/aes.c ../src/utils/common.c ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).c +test-milenage: $(TEST_SRC_MILENAGE) + $(CC) -o test-milenage -Wall -Werror $(TEST_SRC_MILENAGE) \ + -DTEST_MAIN_MILENAGE -I. -DINTERNAL_AES \ + -I../src/crypto -I../src/utils + ./test-milenage + rm test-milenage + +-include $(OBJS:%.o=%.d) diff --git a/contrib/hostapd/README b/hostapd/README index dd242042d4069..eb9aa4815b4f5 100644 --- a/contrib/hostapd/README +++ b/hostapd/README @@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is dual-licensed under both the GPL version 2 and BSD @@ -98,6 +98,10 @@ Current hardware/software requirements: Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo (http://www.prism54.org/) + mac80211-based drivers that support AP mode (with driver=nl80211). + This includes drivers for Atheros (ath9k) and Broadcom (b43) + chipsets. + Any wired Ethernet driver for wired IEEE 802.1X authentication (experimental code) diff --git a/hostapd/README-WPS b/hostapd/README-WPS new file mode 100644 index 0000000000000..b46d767966146 --- /dev/null +++ b/hostapd/README-WPS @@ -0,0 +1,232 @@ +hostapd and Wi-Fi Protected Setup (WPS) +======================================= + +This document describes how the WPS implementation in hostapd can be +configured and how an external component on an AP (e.g., web UI) is +used to enable enrollment of client devices. + + +Introduction to WPS +------------------- + +Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a +wireless network. It allows automated generation of random keys (WPA +passphrase/PSK) and configuration of an access point and client +devices. WPS includes number of methods for setting up connections +with PIN method and push-button configuration (PBC) being the most +commonly deployed options. + +While WPS can enable more home networks to use encryption in the +wireless network, it should be noted that the use of the PIN and +especially PBC mechanisms for authenticating the initial key setup is +not very secure. As such, use of WPS may not be suitable for +environments that require secure network access without chance for +allowing outsiders to gain access during the setup phase. + +WPS uses following terms to describe the entities participating in the +network setup: +- access point: the WLAN access point +- Registrar: a device that control a network and can authorize + addition of new devices); this may be either in the AP ("internal + Registrar") or in an external device, e.g., a laptop, ("external + Registrar") +- Enrollee: a device that is being authorized to use the network + +It should also be noted that the AP and a client device may change +roles (i.e., AP acts as an Enrollee and client device as a Registrar) +when WPS is used to configure the access point. + + +More information about WPS is available from Wi-Fi Alliance: +http://www.wi-fi.org/wifi-protected-setup + + +hostapd implementation +---------------------- + +hostapd includes an optional WPS component that can be used as an +internal WPS Registrar to manage addition of new WPS enabled clients +to the network. In addition, WPS Enrollee functionality in hostapd can +be used to allow external WPS Registrars to configure the access +point, e.g., for initial network setup. In addition, hostapd can proxy a +WPS registration between a wireless Enrollee and an external Registrar +(e.g., Microsoft Vista or Atheros JumpStart) with UPnP. + + +hostapd configuration +--------------------- + +WPS is an optional component that needs to be enabled in hostapd build +configuration (.config). Here is an example configuration that +includes WPS support and uses madwifi driver interface: + +CONFIG_DRIVER_MADWIFI=y +CFLAGS += -I/usr/src/madwifi-0.9.3 +CONFIG_EAP=y +CONFIG_WPS=y +CONFIG_WPS_UPNP=y + + +Following section shows an example runtime configuration +(hostapd.conf) that enables WPS: + +# Configure the driver and network interface +driver=madwifi +interface=ath0 + +# WPA2-Personal configuration for the AP +ssid=wps-test +wpa=2 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=CCMP +# Default WPA passphrase for legacy (non-WPS) clients +wpa_passphrase=12345678 +# Enable random per-device PSK generation for WPS clients +# Please note that the file has to exists for hostapd to start (i.e., create an +# empty file as a starting point). +wpa_psk_file=/etc/hostapd.psk + +# Enable control interface for PBC/PIN entry +ctrl_interface=/var/run/hostapd + +# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup) +eap_server=1 + +# WPS configuration (AP configured, do not allow external WPS Registrars) +wps_state=2 +ap_setup_locked=1 +# If UUID is not configured, it will be generated based on local MAC address. +uuid=87654321-9abc-def0-1234-56789abc0000 +wps_pin_requests=/var/run/hostapd.pin-req +device_name=Wireless AP +manufacturer=Company +model_name=WAP +model_number=123 +serial_number=12345 +device_type=6-0050F204-1 +os_version=01020300 +config_methods=label display push_button keypad + +# if external Registrars are allowed, UPnP support could be added: +#upnp_iface=br0 +#friendly_name=WPS Access Point + + +External operations +------------------- + +WPS requires either a device PIN code (usually, 8-digit number) or a +pushbutton event (for PBC) to allow a new WPS Enrollee to join the +network. hostapd uses the control interface as an input channel for +these events. + +When a client device (WPS Enrollee) connects to hostapd (WPS +Registrar) in order to start PIN mode negotiation for WPS, an +identifier (Enrollee UUID) is sent. hostapd will need to be configured +with a device password (PIN) for this Enrollee. This is an operation +that requires user interaction (assuming there are no pre-configured +PINs on the AP for a set of Enrollee). + +The PIN request with information about the device is appended to the +wps_pin_requests file (/var/run/hostapd.pin-req in this example). In +addition, hostapd control interface event is sent as a notification of +a new device. The AP could use, e.g., a web UI for showing active +Enrollees to the user and request a PIN for an Enrollee. + +The PIN request file has one line for every Enrollee that connected to +the AP, but for which there was no PIN. Following information is +provided for each Enrollee (separated with tabulators): +- timestamp (seconds from 1970-01-01) +- Enrollee UUID +- MAC address +- Device name +- Manufacturer +- Model Name +- Model Number +- Serial Number +- Device category + +Example line in the /var/run/hostapd.pin-req file: +1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1 + +Control interface data: +WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category] +For example: +<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1] + +When the user enters a PIN for a pending Enrollee, e.g., on the web +UI), hostapd needs to be notified of the new PIN over the control +interface. This can be done either by using the UNIX domain socket +-based control interface directly (src/common/wpa_ctrl.c provides +helper functions for using the interface) or by calling hostapd_cli. + +Example command to add a PIN (12345670) for an Enrollee: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670 + +If the UUID-E is not available (e.g., Enrollee waits for the Registrar +to be selected before connecting), wildcard UUID may be used to allow the PIN to be used once with any UUID: + +hostapd_cli wps_pin any 12345670 + + +After this, the Enrollee can connect to the AP again and complete WPS +negotiation. At that point, a new, random WPA PSK is generated for the +client device and the client can then use that key to connect to the +AP to access the network. + + +If the AP includes a pushbutton, WPS PBC mode can be used. It is +enabled by pushing a button on both the AP and the client at about the +same time (2 minute window). hostapd needs to be notified about the AP +button pushed event over the control interface, e.g., by calling +hostapd_cli: + +hostapd_cli wps_pbc + +At this point, the client has two minutes to complete WPS negotiation +which will generate a new WPA PSK in the same way as the PIN method +described above. + + +Credential generation and configuration changes +----------------------------------------------- + +By default, hostapd generates credentials for Enrollees and processing +AP configuration updates internally. However, it is possible to +control these operations from external programs, if desired. + +The internal credential generation can be disabled with +skip_cred_build=1 option in the configuration. extra_cred option will +then need to be used to provide pre-configured Credential attribute(s) +for hostapd to use. The exact data from this binary file will be sent, +i.e., it will have to include valid WPS attributes. extra_cred can +also be used to add additional networks if the Registrar is used to +configure credentials for multiple networks. + +Processing of received configuration updates can be disabled with +wps_cred_processing=1 option. When this is used, an external program +is responsible for creating hostapd configuration files and processing +configuration updates based on messages received from hostapd over +control interface. This will also include the initial configuration on +first successful registration if the AP is initially set in +unconfigured state. + +Following control interface messages are sent out for external programs: + +WPS-REG-SUCCESS <Enrollee MAC address <UUID-E> +For example: +<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333 + +This can be used to tricker change from unconfigured to configured +state (random configuration based on the first successful WPS +registration). In addition, this can be used to update AP UI about the +status of WPS registration progress. + + +WPS-NEW-AP-SETTINGS <hexdump of AP Setup attributes> +For example: +<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844 + +This can be used to update the externally stored AP configuration and +then update hostapd configuration (followed by restarting of hostapd). diff --git a/contrib/hostapd/accounting.c b/hostapd/accounting.c index b22347b2ac2e1..ce71678af4e9b 100644 --- a/contrib/hostapd/accounting.c +++ b/hostapd/accounting.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,11 +13,10 @@ */ #include "includes.h" -#include <assert.h> #include "hostapd.h" -#include "radius.h" -#include "radius_client.h" +#include "radius/radius.h" +#include "radius/radius_client.h" #include "eloop.h" #include "accounting.h" #include "ieee802_1x.h" @@ -29,9 +28,8 @@ * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 -/* from ieee802_1x.c */ -const char *radius_mode_txt(struct hostapd_data *hapd); -int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); +static void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, @@ -54,10 +52,10 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (sta) { radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Acct-Session-Id\n"); goto fail; } @@ -82,10 +80,10 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (sta) { val = ieee802_1x_get_identity(sta->eapol_sm, &len); if (!val) { - snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, - MAC2STR(sta->addr)); + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, + MAC2STR(sta->addr)); val = (u8 *) buf; - len = strlen(buf); + len = os_strlen(buf); } if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, @@ -114,7 +112,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { + os_strlen(hapd->conf->nas_identifier))) { printf("Could not add NAS-Identifier\n"); goto fail; } @@ -125,19 +123,19 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Called-Station-Id\n"); goto fail; } if (sta) { - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Calling-Station-Id\n"); goto fail; } @@ -149,12 +147,12 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } - snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Connect-Info\n"); goto fail; } @@ -177,7 +175,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, fail: radius_msg_free(msg); - free(msg); + os_free(msg); return NULL; } @@ -227,6 +225,11 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) } +/** + * accounting_sta_start - Start STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; @@ -235,6 +238,12 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) if (sta->acct_session_started) return; + accounting_sta_get_id(hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "starting accounting session %08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + time(&sta->acct_session_start); sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; @@ -258,8 +267,8 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) } -void accounting_sta_report(struct hostapd_data *hapd, struct sta_info *sta, - int stop) +static void accounting_sta_report(struct hostapd_data *hapd, + struct sta_info *sta, int stop) { struct radius_msg *msg; int cause = sta->acct_terminate_cause; @@ -355,10 +364,15 @@ void accounting_sta_report(struct hostapd_data *hapd, struct sta_info *sta, fail: radius_msg_free(msg); - free(msg); + os_free(msg); } +/** + * accounting_sta_interim - Send a interim STA accounting report + * @hapd: hostapd BSS data + * @sta: The station + */ void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) @@ -366,17 +380,28 @@ void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) } +/** + * accounting_sta_stop - Stop STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) { accounting_sta_report(hapd, sta, 1); eloop_cancel_timeout(accounting_interim_update, hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "stopped accounting session %08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); sta->acct_session_started = 0; } } -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) +static void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; if (hapd->acct_session_id_lo == 0) { @@ -386,10 +411,19 @@ void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) } -/* Process the RADIUS frames from Accounting Server */ +/** + * accounting_receive - Process the RADIUS frames from Accounting Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ static RadiusRxResult accounting_receive(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, void *data) + const u8 *shared_secret, size_t shared_secret_len, + void *data) { if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { printf("Unknown RADIUS message code\n"); @@ -426,7 +460,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) { printf("Could not add Acct-Terminate-Cause\n"); radius_msg_free(msg); - free(msg); + os_free(msg); return; } @@ -434,6 +468,11 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) } +/** + * accounting_init: Initialize accounting + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ int accounting_init(struct hostapd_data *hapd) { /* Acct-Session-Id should be unique over reboots. If reliable clock is @@ -450,6 +489,10 @@ int accounting_init(struct hostapd_data *hapd) } +/** + * accounting_deinit: Deinitilize accounting + * @hapd: hostapd BSS data + */ void accounting_deinit(struct hostapd_data *hapd) { accounting_report_state(hapd, 0); diff --git a/contrib/hostapd/accounting.h b/hostapd/accounting.h index ee2ee64e3b029..51e6b4d49870e 100644 --- a/contrib/hostapd/accounting.h +++ b/hostapd/accounting.h @@ -18,7 +18,6 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); void accounting_deinit(struct hostapd_data *hapd); int accounting_reconfig(struct hostapd_data *hapd, diff --git a/contrib/hostapd/ap.h b/hostapd/ap.h index b73c5b47a6fb8..98f8ee7449da2 100644 --- a/contrib/hostapd/ap.h +++ b/hostapd/ap.h @@ -1,6 +1,7 @@ /* * hostapd / Station table data structures - * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,6 +16,10 @@ #ifndef AP_H #define AP_H +#ifdef CONFIG_IEEE80211N +#include "ieee802_11_defs.h" +#endif /* CONFIG_IEEE80211N */ + /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) @@ -26,6 +31,10 @@ #define WLAN_STA_SHORT_PREAMBLE BIT(7) #define WLAN_STA_PREAUTH BIT(8) #define WLAN_STA_WME BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) #define WLAN_STA_NONERP BIT(31) /* Maximum number of supported rates (from both Supported Rates and Extended @@ -47,6 +56,9 @@ struct sta_info { unsigned int nonerp_set:1; unsigned int no_short_slot_time_set:1; unsigned int no_short_preamble_set:1; + unsigned int no_ht_gf_set:1; + unsigned int no_ht_set:1; + unsigned int ht_20mhz_set:1; u16 auth_alg; u8 previous_ap[6]; @@ -82,6 +94,22 @@ struct sta_info { struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; + +#ifdef CONFIG_IEEE80211N + struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */ +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_IEEE80211W + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending SA Query + * transaction identifiers */ + struct os_time sa_query_start; +#endif /* CONFIG_IEEE80211W */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ }; diff --git a/contrib/hostapd/ap_list.c b/hostapd/ap_list.c index f2d322125fc85..4f217dc541215 100644 --- a/contrib/hostapd/ap_list.c +++ b/hostapd/ap_list.c @@ -1,8 +1,9 @@ /* * hostapd / AP table - * Copyright 2002-2003, Jouni Malinen <j@w1.fi> - * Copyright 2003-2004, Instant802 Networks, Inc. - * Copyright 2006, Devicescape Software, Inc. + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -107,12 +108,21 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) } +#ifdef CONFIG_IEEE80211N +static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface, + struct ap_info *ap) +{ + return !ap->ht_support; +} +#endif /* CONFIG_IEEE80211N */ + + struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap) { struct ap_info *s; s = iface->ap_hash[STA_HASH(ap)]; - while (s != NULL && memcmp(s->addr, ap, ETH_ALEN) != 0) + while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) s = s->hnext; return s; } @@ -185,13 +195,13 @@ static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) s = iface->ap_hash[STA_HASH(ap->addr)]; if (s == NULL) return; - if (memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { + if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; return; } while (s->hnext != NULL && - memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) + os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; @@ -208,7 +218,7 @@ static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) ap_ap_iter_list_del(iface, ap); iface->num_ap--; - free(ap); + os_free(ap); } @@ -251,12 +261,12 @@ static struct ap_info * ap_ap_add(struct hostapd_iface *iface, u8 *addr) { struct ap_info *ap; - ap = wpa_zalloc(sizeof(struct ap_info)); + ap = os_zalloc(sizeof(struct ap_info)); if (ap == NULL) return NULL; /* initialize AP info data */ - memcpy(ap->addr, addr, ETH_ALEN); + os_memcpy(ap->addr, addr, ETH_ALEN); ap_ap_list_add(iface, ap); iface->num_ap++; ap_ap_hash_add(iface, ap); @@ -282,6 +292,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, struct ap_info *ap; int new_ap = 0; size_t len; + int set_beacon = 0; if (iface->conf->ap_table_max_size < 1) return; @@ -303,18 +314,18 @@ void ap_list_process_beacon(struct hostapd_iface *iface, len = elems->ssid_len; if (len >= sizeof(ap->ssid)) len = sizeof(ap->ssid) - 1; - memcpy(ap->ssid, elems->ssid, len); + os_memcpy(ap->ssid, elems->ssid, len); ap->ssid[len] = '\0'; ap->ssid_len = len; } - memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); + os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); len = 0; if (elems->supp_rates) { len = elems->supp_rates_len; if (len > WLAN_SUPP_RATES_MAX) len = WLAN_SUPP_RATES_MAX; - memcpy(ap->supported_rates, elems->supp_rates, len); + os_memcpy(ap->supported_rates, elems->supp_rates, len); } if (elems->ext_supp_rates) { int len2; @@ -322,7 +333,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface, len2 = WLAN_SUPP_RATES_MAX - len; else len2 = elems->ext_supp_rates_len; - memcpy(ap->supported_rates + len, elems->ext_supp_rates, len2); + os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, + len2); } ap->wpa = elems->wpa_ie != NULL; @@ -337,6 +349,11 @@ void ap_list_process_beacon(struct hostapd_iface *iface, else if (fi) ap->channel = fi->channel; + if (elems->ht_capabilities) + ap->ht_support = 1; + else + ap->ht_support = 0; + ap->num_beacons++; time(&ap->last_beacon); if (fi) { @@ -357,13 +374,24 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (!iface->olbc && ap_list_beacon_olbc(iface, ap)) { - struct hostapd_data *hapd = iface->bss[0]; iface->olbc = 1; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "OLBC AP detected: " MACSTR " - enable " - "protection\n", MAC2STR(ap->addr)); - ieee802_11_set_beacons(hapd->iface); + wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " + "protection", MAC2STR(ap->addr)); + set_beacon++; } + +#ifdef CONFIG_IEEE80211N + if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) { + iface->olbc_ht = 1; + hostapd_ht_operation_update(iface); + wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR + " - enable protection", MAC2STR(ap->addr)); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(iface); } @@ -372,6 +400,7 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_iface *iface = eloop_ctx; time_t now; struct ap_info *ap; + int set_beacon = 0; eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); @@ -395,24 +424,37 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) ap_free_ap(iface, ap); } - if (iface->olbc) { + if (iface->olbc || iface->olbc_ht) { int olbc = 0; + int olbc_ht = 0; + ap = iface->ap_list; - while (ap) { - if (ap_list_beacon_olbc(iface, ap)) { + while (ap && (olbc == 0 || olbc_ht == 0)) { + if (ap_list_beacon_olbc(iface, ap)) olbc = 1; - break; - } +#ifdef CONFIG_IEEE80211N + if (ap_list_beacon_olbc_ht(iface, ap)) + olbc_ht = 1; +#endif /* CONFIG_IEEE80211N */ ap = ap->next; } - if (!olbc) { - struct hostapd_data *hapd = iface->bss[0]; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "OLBC not detected anymore\n"); + if (!olbc && iface->olbc) { + wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); iface->olbc = 0; - ieee802_11_set_beacons(hapd->iface); + set_beacon++; + } +#ifdef CONFIG_IEEE80211N + if (!olbc_ht && iface->olbc_ht) { + wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); + iface->olbc_ht = 0; + hostapd_ht_operation_update(iface); + set_beacon++; } +#endif /* CONFIG_IEEE80211N */ } + + if (set_beacon) + ieee802_11_set_beacons(iface); } diff --git a/contrib/hostapd/ap_list.h b/hostapd/ap_list.h index 668d9096341c9..93704f8bb612f 100644 --- a/contrib/hostapd/ap_list.h +++ b/hostapd/ap_list.h @@ -1,8 +1,9 @@ /* * hostapd / AP table - * Copyright 2002-2003, Jouni Malinen <j@w1.fi> - * Copyright 2003-2004, Instant802 Networks, Inc. - * Copyright 2006, Devicescape Software, Inc. + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -43,6 +44,8 @@ struct ap_info { int datarate; /* in 100 kbps */ int ssi_signal; + int ht_support; + unsigned int num_beacons; /* number of beacon frames received */ time_t last_beacon; diff --git a/contrib/hostapd/beacon.c b/hostapd/beacon.c index 7af2bc1754e2c..31323e864b70b 100644 --- a/contrib/hostapd/beacon.c +++ b/hostapd/beacon.c @@ -2,6 +2,8 @@ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,14 +27,14 @@ #include "hw_features.h" #include "driver.h" #include "sta_info.h" -#include "ieee802_11h.h" +#include "wps_hostapd.h" static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; - if (hapd->iface == NULL || hapd->iface->current_mode == NULL || + if (hapd->iface->current_mode == NULL || hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return 0; @@ -72,7 +74,7 @@ static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) { - if (hapd->iface == NULL || hapd->iface->current_mode == NULL || + if (hapd->iface->current_mode == NULL || hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return eid; @@ -94,27 +96,78 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) } +static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, + struct hostapd_channel_data *start, + struct hostapd_channel_data *prev) +{ + if (end - pos < 3) + return pos; + + /* first channel number */ + *pos++ = start->chan; + /* number of channels */ + *pos++ = (prev->chan - start->chan) / chan_spacing + 1; + /* maximum transmit power level */ + *pos++ = start->max_tx_power; + + return pos; +} + + static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, int max_len) { - int left; u8 *pos = eid; - - if ((!hapd->iconf->ieee80211d && !hapd->iface->dfs_enable) || - max_len < 6) + u8 *end = eid + max_len; + int i; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *start, *prev; + int chan_spacing = 1; + + if (!hapd->iconf->ieee80211d || max_len < 6 || + hapd->iface->current_mode == NULL) return eid; *pos++ = WLAN_EID_COUNTRY; pos++; /* length will be set later */ - memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ + os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ pos += 3; - left = max_len - 3; + + mode = hapd->iface->current_mode; + if (mode->mode == HOSTAPD_MODE_IEEE80211A) + chan_spacing = 4; + + start = prev = NULL; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (start && prev && + prev->chan + chan_spacing == chan->chan && + start->max_tx_power == chan->max_tx_power) { + prev = chan; + continue; /* can use same entry */ + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + start = NULL; + } + + /* Start new group */ + start = prev = chan; + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + } if ((pos - eid) & 1) { - if (left < 1) + if (end - pos < 1) return eid; *pos++ = 0; /* pad for 16-bit alignment */ - left--; } eid[1] = (pos - eid) - 2; @@ -123,45 +176,6 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, } -static u8 * hostapd_eid_power_constraint(struct hostapd_data *hapd, u8 *eid) - -{ - if (!hapd->iface->dfs_enable) - return eid; - *eid++ = WLAN_EID_PWR_CONSTRAINT; - *eid++ = 1; - *eid++ = hapd->iface->pwr_const; - return eid; -} - - -static u8 * hostapd_eid_tpc_report(struct hostapd_data *hapd, u8 *eid) - -{ - if (!hapd->iface->dfs_enable) - return eid; - *eid++ = WLAN_EID_TPC_REPORT; - *eid++ = 2; - *eid++ = hapd->iface->tx_power; /* TX POWER */ - *eid++ = 0; /* Link Margin */ - return eid; -} - -static u8 * hostapd_eid_channel_switch(struct hostapd_data *hapd, u8 *eid) - -{ - if (!hapd->iface->dfs_enable || !hapd->iface->channel_switch) - return eid; - *eid++ = WLAN_EID_CHANNEL_SWITCH; - *eid++ = 3; - *eid++ = CHAN_SWITCH_MODE_QUIET; - *eid++ = hapd->iface->channel_switch; /* New channel */ - /* 0 - very soon; 1 - before next TBTT; num - after num beacons */ - *eid++ = 0; - return eid; -} - - static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, struct sta_info *sta) { @@ -172,7 +186,7 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, if (ie == NULL || ielen > len) return eid; - memcpy(eid, ie, ielen); + os_memcpy(eid, ie, ielen); return eid + ielen; } @@ -183,21 +197,21 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *resp; struct ieee802_11_elems elems; char *ssid; - u8 *pos, *epos; - size_t ssid_len; + u8 *pos, *epos, *ie; + size_t ssid_len, ie_len; struct sta_info *sta = NULL; + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + hostapd_wps_probe_req_rx(hapd, mgmt->sa, ie, ie_len); + if (!hapd->iconf->send_probe_response) return; - if (ieee802_11_parse_elems(hapd, mgmt->u.probe_req.variable, - len - (IEEE80211_HDRLEN + - sizeof(mgmt->u.probe_req)), &elems, - 0) - == ParseFailed) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Could not parse ProbeReq from " MACSTR "\n", - MAC2STR(mgmt->sa)); + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, + MAC2STR(mgmt->sa)); return; } @@ -205,15 +219,15 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, ssid_len = 0; if ((!elems.ssid || !elems.supp_rates)) { - printf("STA " MACSTR " sent probe request without SSID or " - "supported rates element\n", MAC2STR(mgmt->sa)); + wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " + "without SSID or supported rates element", + MAC2STR(mgmt->sa)); return; } if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, - "Probe Request from " MACSTR " for broadcast " - "SSID ignored\n", MAC2STR(mgmt->sa)); + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " + "broadcast SSID ignored", MAC2STR(mgmt->sa)); return; } @@ -221,7 +235,8 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, if (elems.ssid_len == 0 || (elems.ssid_len == hapd->conf->ssid.ssid_len && - memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == 0)) { + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == + 0)) { ssid = hapd->conf->ssid.ssid; ssid_len = hapd->conf->ssid.ssid_len; if (sta) @@ -229,29 +244,31 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, } if (!ssid) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) { - printf("Probe Request from " MACSTR " for foreign " - "SSID '", MAC2STR(mgmt->sa)); - ieee802_11_print_ssid(elems.ssid, elems.ssid_len); - printf("'\n"); + if (!(mgmt->da[0] & 0x01)) { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, + elems.ssid_len); + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for foreign SSID '%s'", + MAC2STR(mgmt->sa), ssid_txt); } return; } /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ -#define MAX_PROBERESP_LEN 512 - resp = wpa_zalloc(MAX_PROBERESP_LEN); +#define MAX_PROBERESP_LEN 768 + resp = os_zalloc(MAX_PROBERESP_LEN); if (resp == NULL) return; epos = ((u8 *) resp) + MAX_PROBERESP_LEN; resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_PROBE_RESP); - memcpy(resp->da, mgmt->sa, ETH_ALEN); - memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->da, mgmt->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); - memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.probe_resp.beacon_int = host_to_le16(hapd->iconf->beacon_int); @@ -262,7 +279,7 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; *pos++ = ssid_len; - memcpy(pos, ssid, ssid_len); + os_memcpy(pos, ssid, ssid_len); pos += ssid_len; /* Supported rates */ @@ -273,9 +290,6 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, pos = hostapd_eid_country(hapd, pos, epos - pos); - pos = hostapd_eid_power_constraint(hapd, pos); - pos = hostapd_eid_tpc_report(hapd, pos); - /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -285,18 +299,27 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); /* Wi-Fi Wireless Multimedia Extensions */ - if (hapd->conf->wme_enabled) - pos = hostapd_eid_wme(hapd, pos); + pos = hostapd_eid_wme(hapd, pos); + + pos = hostapd_eid_ht_capabilities_info(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { + os_memcpy(pos, hapd->wps_probe_resp_ie, + hapd->wps_probe_resp_ie_len); + pos += hapd->wps_probe_resp_ie_len; + } +#endif /* CONFIG_WPS */ if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0) perror("handle_probe_req: send"); - free(resp); + os_free(resp); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, "STA " MACSTR - " sent probe request for %s SSID\n", - MAC2STR(mgmt->sa), elems.ssid_len == 0 ? "broadcast" : - "our"); + wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " + "SSID", MAC2STR(mgmt->sa), + elems.ssid_len == 0 ? "broadcast" : "our"); } @@ -311,23 +334,23 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) ERP_INFO_USE_PROTECTION) ? 1 : 0); #define BEACON_HEAD_BUF_SIZE 256 -#define BEACON_TAIL_BUF_SIZE 256 - head = wpa_zalloc(BEACON_HEAD_BUF_SIZE); - tailpos = tail = malloc(BEACON_TAIL_BUF_SIZE); +#define BEACON_TAIL_BUF_SIZE 512 + head = os_zalloc(BEACON_HEAD_BUF_SIZE); + tailpos = tail = os_malloc(BEACON_TAIL_BUF_SIZE); if (head == NULL || tail == NULL) { - printf("Failed to set beacon data\n"); - free(head); - free(tail); + wpa_printf(MSG_ERROR, "Failed to set beacon data"); + os_free(head); + os_free(tail); return; } head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_BEACON); head->duration = host_to_le16(0); - memset(head->da, 0xff, ETH_ALEN); + os_memset(head->da, 0xff, ETH_ALEN); - memcpy(head->sa, hapd->own_addr, ETH_ALEN); - memcpy(head->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); head->u.beacon.beacon_int = host_to_le16(hapd->iconf->beacon_int); @@ -341,13 +364,14 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->conf->ignore_broadcast_ssid == 2) { /* clear the data, but keep the correct length of the SSID */ *pos++ = hapd->conf->ssid.ssid_len; - memset(pos, 0, hapd->conf->ssid.ssid_len); + os_memset(pos, 0, hapd->conf->ssid.ssid_len); pos += hapd->conf->ssid.ssid_len; } else if (hapd->conf->ignore_broadcast_ssid) { *pos++ = 0; /* empty SSID */ } else { *pos++ = hapd->conf->ssid.ssid_len; - memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); + os_memcpy(pos, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); pos += hapd->conf->ssid.ssid_len; } @@ -362,10 +386,6 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_country(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); - tailpos = hostapd_eid_power_constraint(hapd, tailpos); - tailpos = hostapd_eid_channel_switch(hapd, tailpos); - tailpos = hostapd_eid_tpc_report(hapd, tailpos); - /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -376,36 +396,64 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos, NULL); /* Wi-Fi Wireless Multimedia Extensions */ - if (hapd->conf->wme_enabled) - tailpos = hostapd_eid_wme(hapd, tailpos); + tailpos = hostapd_eid_wme(hapd, tailpos); + +#ifdef CONFIG_IEEE80211N + if (hapd->iconf->ieee80211n) { + u8 *ht_capab, *ht_oper; + ht_capab = tailpos; + tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos); + + ht_oper = tailpos; + tailpos = hostapd_eid_ht_operation(hapd, tailpos); + + if (tailpos > ht_oper && ht_oper > ht_capab && + hostapd_set_ht_params(hapd->conf->iface, hapd, + ht_capab + 2, ht_capab[1], + ht_oper + 2, ht_oper[1])) { + wpa_printf(MSG_ERROR, "Could not set HT capabilities " + "for kernel driver"); + } + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_beacon_ie) { + os_memcpy(tailpos, hapd->wps_beacon_ie, + hapd->wps_beacon_ie_len); + tailpos += hapd->wps_beacon_ie_len; + } +#endif /* CONFIG_WPS */ tail_len = tailpos > tail ? tailpos - tail : 0; if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len, tail, tail_len)) - printf("Failed to set beacon head/tail\n"); + wpa_printf(MSG_ERROR, "Failed to set beacon head/tail"); - free(tail); - free(head); + os_free(tail); + os_free(head); if (hostapd_set_cts_protect(hapd, cts_protection)) - printf("Failed to set CTS protect in kernel driver\n"); + wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " + "driver"); - if (hapd->iface && hapd->iface->current_mode && + if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hostapd_set_short_slot_time(hapd, hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1)) - printf("Failed to set Short Slot Time option in kernel " - "driver\n"); + wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " + "in kernel driver"); - if (hapd->iface && hapd->iface->num_sta_no_short_preamble == 0 && + if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) preamble = SHORT_PREAMBLE; else preamble = LONG_PREAMBLE; if (hostapd_set_preamble(hapd, preamble)) - printf("Could not set preamble for kernel driver\n"); + wpa_printf(MSG_ERROR, "Could not set preamble for kernel " + "driver"); } diff --git a/contrib/hostapd/beacon.h b/hostapd/beacon.h index 18e0da2e89f68..18e0da2e89f68 100644 --- a/contrib/hostapd/beacon.h +++ b/hostapd/beacon.h diff --git a/hostapd/config.c b/hostapd/config.c new file mode 100644 index 0000000000000..6ad14d2d4cfa8 --- /dev/null +++ b/hostapd/config.c @@ -0,0 +1,2617 @@ +/* + * hostapd / Configuration file + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <grp.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "hostapd.h" +#include "driver.h" +#include "sha1.h" +#include "eap_server/eap.h" +#include "radius/radius_client.h" +#include "wpa_common.h" +#include "wpa.h" +#include "uuid.h" +#include "eap_common/eap_wsc_common.h" + + +#define MAX_STA_COUNT 2007 + +extern struct wpa_driver_ops *hostapd_drivers[]; + + +static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, + const char *fname) +{ + FILE *f; + char buf[128], *pos, *pos2; + int line = 0, vlan_id; + struct hostapd_vlan *vlan; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (buf[0] == '*') { + vlan_id = VLAN_ID_WILDCARD; + pos = buf + 1; + } else { + vlan_id = strtol(buf, &pos, 10); + if (buf == pos || vlan_id < 1 || + vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Invalid VLAN ID at " + "line %d in '%s'", line, fname); + fclose(f); + return -1; + } + } + + while (*pos == ' ' || *pos == '\t') + pos++; + pos2 = pos; + while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') + pos2++; + *pos2 = '\0'; + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " + "in '%s'", line, fname); + fclose(f); + return -1; + } + + vlan = os_malloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while reading " + "VLAN interfaces from '%s'", fname); + fclose(f); + return -1; + } + + os_memset(vlan, 0, sizeof(*vlan)); + vlan->vlan_id = vlan_id; + os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); + if (bss->vlan_tail) + bss->vlan_tail->next = vlan; + else + bss->vlan = vlan; + bss->vlan_tail = vlan; + } + + fclose(f); + + return 0; +} + + +static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) +{ + struct hostapd_vlan *vlan, *prev; + + vlan = bss->vlan; + prev = NULL; + while (vlan) { + prev = vlan; + vlan = vlan->next; + os_free(prev); + } + + bss->vlan = NULL; +} + + +/* convert floats with one decimal place to value*10 int, i.e., + * "1.5" will return 15 */ +static int hostapd_config_read_int10(const char *value) +{ + int i, d; + char *pos; + + i = atoi(value); + pos = os_strchr(value, '.'); + d = 0; + if (pos) { + pos++; + if (*pos >= '0' && *pos <= '9') + d = *pos - '0'; + } + + return i * 10 + d; +} + + +static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) +{ + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; + bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; + bss->logger_syslog = (unsigned int) -1; + bss->logger_stdout = (unsigned int) -1; + + bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED; + + bss->wep_rekeying_period = 300; + /* use key0 in individual key and key1 in broadcast key */ + bss->broadcast_key_idx_min = 1; + bss->broadcast_key_idx_max = 2; + bss->eap_reauth_period = 3600; + + bss->wpa_group_rekey = 600; + bss->wpa_gmk_rekey = 86400; + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + bss->wpa_pairwise = WPA_CIPHER_TKIP; + bss->wpa_group = WPA_CIPHER_TKIP; + bss->rsn_pairwise = 0; + + bss->max_num_sta = MAX_STA_COUNT; + + bss->dtim_period = 2; + + bss->radius_server_auth_port = 1812; + bss->ap_max_inactivity = AP_MAX_INACTIVITY; + bss->eapol_version = EAPOL_VERSION; + + bss->max_listen_interval = 65535; + +#ifdef CONFIG_IEEE80211W + bss->assoc_sa_query_max_timeout = 1000; + bss->assoc_sa_query_retry_timeout = 201; +#endif /* CONFIG_IEEE80211W */ +#ifdef EAP_FAST + /* both anonymous and authenticated provisioning */ + bss->eap_fast_prov = 3; + bss->pac_key_lifetime = 7 * 24 * 60 * 60; + bss->pac_key_refresh_time = 1 * 24 * 60 * 60; +#endif /* EAP_FAST */ +} + + +static struct hostapd_config * hostapd_config_defaults(void) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + int i; + const int aCWmin = 15, aCWmax = 1024; + const struct hostapd_wme_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wme_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wme_ac_params ac_vi = /* video traffic */ + { aCWmin >> 1, aCWmin, 2, 3000 / 32, 1 }; + const struct hostapd_wme_ac_params ac_vo = /* voice traffic */ + { aCWmin >> 2, aCWmin >> 1, 2, 1500 / 32, 1 }; + + conf = os_zalloc(sizeof(*conf)); + bss = os_zalloc(sizeof(*bss)); + if (conf == NULL || bss == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "configuration data."); + os_free(conf); + os_free(bss); + return NULL; + } + + /* set default driver based on configuration */ + conf->driver = hostapd_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + os_free(conf); + os_free(bss); + return NULL; + } + + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + + hostapd_config_defaults_bss(bss); + + conf->num_bss = 1; + conf->bss = bss; + + conf->beacon_int = 100; + conf->rts_threshold = -1; /* use driver default: 2347 */ + conf->fragm_threshold = -1; /* user driver default: 2346 */ + conf->send_probe_response = 1; + conf->bridge_packets = INTERNAL_BRIDGE_DO_NOT_CONTROL; + + for (i = 0; i < NUM_TX_QUEUES; i++) + conf->tx_queue[i].aifs = -1; /* use hw default */ + + conf->wme_ac_params[0] = ac_be; + conf->wme_ac_params[1] = ac_bk; + conf->wme_ac_params[2] = ac_vi; + conf->wme_ac_params[3] = ac_vo; + +#ifdef CONFIG_IEEE80211N + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; +#endif /* CONFIG_IEEE80211N */ + + return conf; +} + + +int hostapd_mac_comp(const void *a, const void *b) +{ + return os_memcmp(a, b, sizeof(macaddr)); +} + + +int hostapd_mac_comp_empty(const void *a) +{ + macaddr empty = { 0 }; + return os_memcmp(a, empty, sizeof(macaddr)); +} + + +static int hostapd_acl_comp(const void *a, const void *b) +{ + const struct mac_acl_entry *aa = a; + const struct mac_acl_entry *bb = b; + return os_memcmp(aa->addr, bb->addr, sizeof(macaddr)); +} + + +static int hostapd_config_read_maclist(const char *fname, + struct mac_acl_entry **acl, int *num) +{ + FILE *f; + char buf[128], *pos; + int line = 0; + u8 addr[ETH_ALEN]; + struct mac_acl_entry *newacl; + int vlan_id; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (hwaddr_aton(buf, addr)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at " + "line %d in '%s'", buf, line, fname); + fclose(f); + return -1; + } + + vlan_id = 0; + pos = buf; + while (*pos != '\0' && *pos != ' ' && *pos != '\t') + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos != '\0') + vlan_id = atoi(pos); + + newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl)); + if (newacl == NULL) { + wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + fclose(f); + return -1; + } + + *acl = newacl; + os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); + (*acl)[*num].vlan_id = vlan_id; + (*num)++; + } + + fclose(f); + + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + + return 0; +} + + +static int hostapd_config_read_wpa_psk(const char *fname, + struct hostapd_ssid *ssid) +{ + FILE *f; + char buf[128], *pos; + int line = 0, ret = 0, len, ok; + u8 addr[ETH_ALEN]; + struct hostapd_wpa_psk *psk; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (hwaddr_aton(buf, addr)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " + "line %d in '%s'", buf, line, fname); + ret = -1; + break; + } + + psk = os_zalloc(sizeof(*psk)); + if (psk == NULL) { + wpa_printf(MSG_ERROR, "WPA PSK allocation failed"); + ret = -1; + break; + } + if (is_zero_ether_addr(addr)) + psk->group = 1; + else + os_memcpy(psk->addr, addr, ETH_ALEN); + + pos = buf + 17; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + pos++; + + ok = 0; + len = os_strlen(pos); + if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) + ok = 1; + else if (len >= 8 && len < 64) { + pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, + 4096, psk->psk, PMK_LEN); + ok = 1; + } + if (!ok) { + wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in " + "'%s'", pos, line, fname); + os_free(psk); + ret = -1; + break; + } + + psk->next = ssid->wpa_psk; + ssid->wpa_psk = psk; + } + + fclose(f); + + return ret; +} + + +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) +{ + struct hostapd_ssid *ssid = &conf->ssid; + + if (ssid->wpa_passphrase != NULL) { + if (ssid->wpa_psk != NULL) { + wpa_printf(MSG_ERROR, "Warning: both WPA PSK and " + "passphrase set. Using passphrase."); + os_free(ssid->wpa_psk); + } + ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (ssid->wpa_psk == NULL) { + wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "SSID", + (u8 *) ssid->ssid, ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", + (u8 *) ssid->wpa_passphrase, + os_strlen(ssid->wpa_passphrase)); + pbkdf2_sha1(ssid->wpa_passphrase, + ssid->ssid, ssid->ssid_len, + 4096, ssid->wpa_psk->psk, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", + ssid->wpa_psk->psk, PMK_LEN); + ssid->wpa_psk->group = 1; + } + + if (ssid->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, + &conf->ssid)) + return -1; + } + + return 0; +} + + +#ifdef EAP_SERVER +static int hostapd_config_read_eap_user(const char *fname, + struct hostapd_bss_config *conf) +{ + FILE *f; + char buf[512], *pos, *start, *pos2; + int line = 0, ret = 0, num_methods; + struct hostapd_eap_user *user, *tail = NULL; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); + return -1; + } + + /* Lines: "user" METHOD,METHOD2 "password" (password optional) */ + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + user = NULL; + + if (buf[0] != '"' && buf[0] != '*') { + wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in " + "start) on line %d in '%s'", line, fname); + goto failed; + } + + user = os_zalloc(sizeof(*user)); + if (user == NULL) { + wpa_printf(MSG_ERROR, "EAP user allocation failed"); + goto failed; + } + user->force_version = -1; + + if (buf[0] == '*') { + pos = buf; + } else { + pos = buf + 1; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP identity " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->identity = os_malloc(pos - start); + if (user->identity == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP identity"); + goto failed; + } + os_memcpy(user->identity, start, pos - start); + user->identity_len = pos - start; + + if (pos[0] == '"' && pos[1] == '*') { + user->wildcard_prefix = 1; + pos++; + } + } + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No EAP method on line %d in " + "'%s'", line, fname); + goto failed; + } + + start = pos; + while (*pos != ' ' && *pos != '\t' && *pos != '\0') + pos++; + if (*pos == '\0') { + pos = NULL; + } else { + *pos = '\0'; + pos++; + } + num_methods = 0; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) { + *pos3++ = '\0'; + } + user->methods[num_methods].method = + eap_server_get_type( + start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == + EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) + { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_ERROR, "Unsupported EAP type " + "'%s' on line %d in '%s'", + start, line, fname); + goto failed; + } + + num_methods++; + if (num_methods >= EAP_USER_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + if (num_methods == 0 && user->ttls_auth == 0) { + wpa_printf(MSG_ERROR, "No EAP types configured on " + "line %d in '%s'", line, fname); + goto failed; + } + + if (pos == NULL) + goto done; + + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '\0') + goto done; + + if (os_strncmp(pos, "[ver=0]", 7) == 0) { + user->force_version = 0; + goto done; + } + + if (os_strncmp(pos, "[ver=1]", 7) == 0) { + user->force_version = 1; + goto done; + } + + if (os_strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + goto done; + } + + if (*pos == '"') { + pos++; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP password " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->password = os_malloc(pos - start); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + os_memcpy(user->password, start, pos - start); + user->password_len = pos - start; + + pos++; + } else if (os_strncmp(pos, "hash:", 5) == 0) { + pos += 5; + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if (pos2 - pos != 32) { + wpa_printf(MSG_ERROR, "Invalid password hash " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc(16); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password hash"); + goto failed; + } + if (hexstr2bin(pos, user->password, 16) < 0) { + wpa_printf(MSG_ERROR, "Invalid hash password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = 16; + user->password_hash = 1; + pos = pos2; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if ((pos2 - pos) & 1) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc((pos2 - pos) / 2); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + if (hexstr2bin(pos, user->password, + (pos2 - pos) / 2) < 0) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = (pos2 - pos) / 2; + pos = pos2; + } + + while (*pos == ' ' || *pos == '\t') + pos++; + if (os_strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + } + + done: + if (tail == NULL) { + tail = conf->eap_user = user; + } else { + tail->next = user; + tail = user; + } + continue; + + failed: + if (user) { + os_free(user->password); + os_free(user->identity); + os_free(user); + } + ret = -1; + break; + } + + fclose(f); + + return ret; +} +#endif /* EAP_SERVER */ + + +static int +hostapd_config_read_radius_addr(struct hostapd_radius_server **server, + int *num_server, const char *val, int def_port, + struct hostapd_radius_server **curr_serv) +{ + struct hostapd_radius_server *nserv; + int ret; + static int server_index = 1; + + nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv)); + if (nserv == NULL) + return -1; + + *server = nserv; + nserv = &nserv[*num_server]; + (*num_server)++; + (*curr_serv) = nserv; + + os_memset(nserv, 0, sizeof(*nserv)); + nserv->port = def_port; + ret = hostapd_parse_ip_addr(val, &nserv->addr); + nserv->index = server_index++; + + return ret; +} + + +static int hostapd_config_parse_key_mgmt(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_IEEE80211R + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) + val |= WPA_KEY_MGMT_PSK_SHA256; + else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SHA256; +#endif /* CONFIG_IEEE80211W */ + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + + os_free(buf); + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values " + "configured.", line); + return -1; + } + + return val; +} + + +static int hostapd_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf) +{ + if (bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; + } + + if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && + bss->ssid.wpa_psk_file == NULL) { + wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " + "is not configured."); + return -1; + } + + if (hostapd_mac_comp_empty(bss->bssid) != 0) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if ((&conf->bss[i] != bss) && + (hostapd_mac_comp(conf->bss[i].bssid, + bss->bssid) == 0)) { + wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR + " on interface '%s' and '%s'.", + MAC2STR(bss->bssid), + conf->bss[i].iface, bss->iface); + return -1; + } + } + } + +#ifdef CONFIG_IEEE80211R + if ((bss->wpa_key_mgmt & + (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X)) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || + os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { + wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " + "nas_identifier to be configured as a 1..48 octet " + "string"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if (conf->ieee80211n && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) { + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " + "requires CCMP to be enabled"); + return -1; + } +#endif /* CONFIG_IEEE80211N */ + + return 0; +} + + +static int hostapd_config_check(struct hostapd_config *conf) +{ + size_t i; + + if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " + "setting the country_code"); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_config_check_bss(&conf->bss[i], conf)) + return -1; + } + + return 0; +} + + +static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, + char *val) +{ + size_t len = os_strlen(val); + + if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) + return -1; + + if (val[0] == '"') { + if (len < 2 || val[len - 1] != '"') + return -1; + len -= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + os_memcpy(wep->key[keyidx], val + 1, len); + wep->len[keyidx] = len; + } else { + if (len & 1) + return -1; + len /= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + wep->len[keyidx] = len; + if (hexstr2bin(val, wep->key[keyidx], len) < 0) + return -1; + } + + wep->keys_set++; + + return 0; +} + + +static int hostapd_parse_rates(int **rate_list, char *val) +{ + int *list; + int count; + char *pos, *end; + + os_free(*rate_list); + *rate_list = NULL; + + pos = val; + count = 0; + while (*pos != '\0') { + if (*pos == ' ') + count++; + pos++; + } + + list = os_malloc(sizeof(int) * (count + 2)); + if (list == NULL) + return -1; + pos = val; + count = 0; + while (*pos != '\0') { + end = os_strchr(pos, ' '); + if (end) + *end = '\0'; + + list[count++] = atoi(pos); + if (!end) + break; + pos = end + 1; + } + list[count] = -1; + + *rate_list = list; + return 0; +} + + +static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) +{ + struct hostapd_bss_config *bss; + + if (*ifname == '\0') + return -1; + + bss = os_realloc(conf->bss, (conf->num_bss + 1) * + sizeof(struct hostapd_bss_config)); + if (bss == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS entry"); + return -1; + } + conf->bss = bss; + + bss = &(conf->bss[conf->num_bss]); + os_memset(bss, 0, sizeof(*bss)); + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS RADIUS data"); + return -1; + } + + conf->num_bss++; + conf->last_bss = bss; + + hostapd_config_defaults_bss(bss); + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1); + + return 0; +} + + +static int valid_cw(int cw) +{ + return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || + cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); +} + + +enum { + IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ + IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ + IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ + IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */ + IEEE80211_TX_QUEUE_DATA4 = 4, + IEEE80211_TX_QUEUE_AFTER_BEACON = 6, + IEEE80211_TX_QUEUE_BEACON = 7 +}; + +static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, + char *val) +{ + int num; + char *pos; + struct hostapd_tx_queue_params *queue; + + /* skip 'tx_queue_' prefix */ + pos = name + 9; + if (os_strncmp(pos, "data", 4) == 0 && + pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { + num = pos[4] - '0'; + pos += 6; + } else if (os_strncmp(pos, "after_beacon_", 13) == 0) { + num = IEEE80211_TX_QUEUE_AFTER_BEACON; + pos += 13; + } else if (os_strncmp(pos, "beacon_", 7) == 0) { + num = IEEE80211_TX_QUEUE_BEACON; + pos += 7; + } else { + wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); + return -1; + } + + queue = &conf->tx_queue[num]; + + if (os_strcmp(pos, "aifs") == 0) { + queue->aifs = atoi(val); + if (queue->aifs < 0 || queue->aifs > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", + queue->aifs); + return -1; + } + } else if (os_strcmp(pos, "cwmin") == 0) { + queue->cwmin = atoi(val); + if (!valid_cw(queue->cwmin)) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", + queue->cwmin); + return -1; + } + } else if (os_strcmp(pos, "cwmax") == 0) { + queue->cwmax = atoi(val); + if (!valid_cw(queue->cwmax)) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", + queue->cwmax); + return -1; + } + } else if (os_strcmp(pos, "burst") == 0) { + queue->burst = hostapd_config_read_int10(val); + } else { + wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos); + return -1; + } + + queue->configured = 1; + + return 0; +} + + +static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, + char *val) +{ + int num, v; + char *pos; + struct hostapd_wme_ac_params *ac; + + /* skip 'wme_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown wme name '%s'", pos); + return -1; + } + + ac = &conf->wme_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txopLimit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wme_ac_ field '%s'", pos); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211R +static int add_r0kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r0kh *r0kh; + char *pos, *next; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (r0kh == NULL) + return -1; + + /* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r0kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos); + os_free(r0kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos); + os_free(r0kh); + return -1; + } + r0kh->id_len = next - pos - 1; + os_memcpy(r0kh->id, pos, r0kh->id_len); + + pos = next; + if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { + wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); + os_free(r0kh); + return -1; + } + + r0kh->next = bss->r0kh_list; + bss->r0kh_list = r0kh; + + return 0; +} + + +static int add_r1kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r1kh *r1kh; + char *pos, *next; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (r1kh == NULL) + return -1; + + /* 02:01:02:03:04:05 02:01:02:03:04:05 + * 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->id)) { + wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { + wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); + os_free(r1kh); + return -1; + } + + r1kh->next = bss->r1kh_list; + bss->r1kh_list = r1kh; + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_IEEE80211N +static int hostapd_config_ht_capab(struct hostapd_config *conf, + const char *capab) +{ + if (os_strstr(capab, "[LDPC]")) + conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP; + if (os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = -1; + } + if (os_strstr(capab, "[HT40+]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = 1; + } + if (os_strstr(capab, "[SMPS-STATIC]")) { + conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; + conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; + } + if (os_strstr(capab, "[SMPS-DYNAMIC]")) { + conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; + conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC; + } + if (os_strstr(capab, "[GF]")) + conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD; + if (os_strstr(capab, "[SHORT-GI-20]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ; + if (os_strstr(capab, "[SHORT-GI-40]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ; + if (os_strstr(capab, "[TX-STBC]")) + conf->ht_capab |= HT_CAP_INFO_TX_STBC; + if (os_strstr(capab, "[RX-STBC1]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_1; + } + if (os_strstr(capab, "[RX-STBC12]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_12; + } + if (os_strstr(capab, "[RX-STBC123]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_123; + } + if (os_strstr(capab, "[DELAYED-BA]")) + conf->ht_capab |= HT_CAP_INFO_DELAYED_BA; + if (os_strstr(capab, "[MAX-AMSDU-7935]")) + conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE; + if (os_strstr(capab, "[DSSS_CCK-40]")) + conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ; + if (os_strstr(capab, "[PSMP]")) + conf->ht_capab |= HT_CAP_INFO_PSMP_SUPP; + if (os_strstr(capab, "[LSIG-TXOP-PROT]")) + conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT; + + return 0; +} +#endif /* CONFIG_IEEE80211N */ + + +/** + * hostapd_config_read - Read and parse a configuration file + * @fname: Configuration file name (including path, if needed) + * Returns: Allocated configuration data structure + */ +struct hostapd_config * hostapd_config_read(const char *fname) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + FILE *f; + char buf[256], *pos; + int line = 0; + int errors = 0; + int pairwise; + size_t i; + + f = fopen(fname, "r"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " + "for reading.", fname); + return NULL; + } + + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return NULL; + } + bss = conf->last_bss = conf->bss; + + while (fgets(buf, sizeof(buf), f)) { + bss = conf->last_bss; + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + pos = os_strchr(buf, '='); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", + line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + + if (os_strcmp(buf, "interface") == 0) { + os_strlcpy(conf->bss[0].iface, pos, + sizeof(conf->bss[0].iface)); + } else if (os_strcmp(buf, "bridge") == 0) { + os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "driver") == 0) { + int i; + /* clear to get error below if setting is invalid */ + conf->driver = NULL; + for (i = 0; hostapd_drivers[i]; i++) { + if (os_strcmp(pos, hostapd_drivers[i]->name) == + 0) { + conf->driver = hostapd_drivers[i]; + break; + } + } + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid/" + "unknown driver '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "debug") == 0) { + wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' " + "configuration variable is not used " + "anymore", line); + } else if (os_strcmp(buf, "logger_syslog_level") == 0) { + bss->logger_syslog_level = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout_level") == 0) { + bss->logger_stdout_level = atoi(pos); + } else if (os_strcmp(buf, "logger_syslog") == 0) { + bss->logger_syslog = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout") == 0) { + bss->logger_stdout = atoi(pos); + } else if (os_strcmp(buf, "dump_file") == 0) { + bss->dump_log_name = os_strdup(pos); + } else if (os_strcmp(buf, "ssid") == 0) { + bss->ssid.ssid_len = os_strlen(pos); + if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || + bss->ssid.ssid_len < 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID " + "'%s'", line, pos); + errors++; + } else { + os_memcpy(bss->ssid.ssid, pos, + bss->ssid.ssid_len); + bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; + bss->ssid.ssid_set = 1; + } + } else if (os_strcmp(buf, "macaddr_acl") == 0) { + bss->macaddr_acl = atoi(pos); + if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && + bss->macaddr_acl != DENY_UNLESS_ACCEPTED && + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "macaddr_acl %d", + line, bss->macaddr_acl); + } + } else if (os_strcmp(buf, "accept_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->accept_mac, + &bss->num_accept_mac)) + { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "read accept_mac_file '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "deny_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->deny_mac, + &bss->num_deny_mac)) { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "read deny_mac_file '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { + bss->ap_max_inactivity = atoi(pos); + } else if (os_strcmp(buf, "country_code") == 0) { + os_memcpy(conf->country, pos, 2); + /* FIX: make this configurable */ + conf->country[2] = ' '; + } else if (os_strcmp(buf, "ieee80211d") == 0) { + conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee8021x") == 0) { + bss->ieee802_1x = atoi(pos); + } else if (os_strcmp(buf, "eapol_version") == 0) { + bss->eapol_version = atoi(pos); + if (bss->eapol_version < 1 || + bss->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL " + "version (%d): '%s'.", + line, bss->eapol_version, pos); + errors++; + } else + wpa_printf(MSG_DEBUG, "eapol_version=%d", + bss->eapol_version); +#ifdef EAP_SERVER + } else if (os_strcmp(buf, "eap_authenticator") == 0) { + bss->eap_server = atoi(pos); + wpa_printf(MSG_ERROR, "Line %d: obsolete " + "eap_authenticator used; this has been " + "renamed to eap_server", line); + } else if (os_strcmp(buf, "eap_server") == 0) { + bss->eap_server = atoi(pos); + } else if (os_strcmp(buf, "eap_user_file") == 0) { + if (hostapd_config_read_eap_user(pos, bss)) + errors++; + } else if (os_strcmp(buf, "ca_cert") == 0) { + os_free(bss->ca_cert); + bss->ca_cert = os_strdup(pos); + } else if (os_strcmp(buf, "server_cert") == 0) { + os_free(bss->server_cert); + bss->server_cert = os_strdup(pos); + } else if (os_strcmp(buf, "private_key") == 0) { + os_free(bss->private_key); + bss->private_key = os_strdup(pos); + } else if (os_strcmp(buf, "private_key_passwd") == 0) { + os_free(bss->private_key_passwd); + bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "check_crl") == 0) { + bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "dh_file") == 0) { + os_free(bss->dh_file); + bss->dh_file = os_strdup(pos); +#ifdef EAP_FAST + } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) { + os_free(bss->pac_opaque_encr_key); + bss->pac_opaque_encr_key = os_malloc(16); + if (bss->pac_opaque_encr_key == NULL) { + wpa_printf(MSG_ERROR, "Line %d: No memory for " + "pac_opaque_encr_key", line); + errors++; + } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, + 16)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "pac_opaque_encr_key", line); + errors++; + } + } else if (os_strcmp(buf, "eap_fast_a_id") == 0) { + size_t idlen = os_strlen(pos); + if (idlen & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "eap_fast_a_id", line); + errors++; + } else { + os_free(bss->eap_fast_a_id); + bss->eap_fast_a_id = os_malloc(idlen / 2); + if (bss->eap_fast_a_id == NULL || + hexstr2bin(pos, bss->eap_fast_a_id, + idlen / 2)) { + wpa_printf(MSG_ERROR, "Line %d: " + "Failed to parse " + "eap_fast_a_id", line); + errors++; + } else + bss->eap_fast_a_id_len = idlen / 2; + } + } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) { + os_free(bss->eap_fast_a_id_info); + bss->eap_fast_a_id_info = os_strdup(pos); + } else if (os_strcmp(buf, "eap_fast_prov") == 0) { + bss->eap_fast_prov = atoi(pos); + } else if (os_strcmp(buf, "pac_key_lifetime") == 0) { + bss->pac_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { + bss->pac_key_refresh_time = atoi(pos); +#endif /* EAP_FAST */ +#ifdef EAP_SIM + } else if (os_strcmp(buf, "eap_sim_db") == 0) { + os_free(bss->eap_sim_db); + bss->eap_sim_db = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { + bss->eap_sim_aka_result_ind = atoi(pos); +#endif /* EAP_SIM */ +#ifdef EAP_TNC + } else if (os_strcmp(buf, "tnc") == 0) { + bss->tnc = atoi(pos); +#endif /* EAP_TNC */ +#endif /* EAP_SERVER */ + } else if (os_strcmp(buf, "eap_message") == 0) { + char *term; + bss->eap_req_id_text = os_strdup(pos); + if (bss->eap_req_id_text == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "allocate memory for " + "eap_req_id_text", line); + errors++; + continue; + } + bss->eap_req_id_text_len = + os_strlen(bss->eap_req_id_text); + term = os_strstr(bss->eap_req_id_text, "\\0"); + if (term) { + *term++ = '\0'; + os_memmove(term, term + 1, + bss->eap_req_id_text_len - + (term - bss->eap_req_id_text) - 1); + bss->eap_req_id_text_len--; + } + } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { + bss->default_wep_key_len = atoi(pos); + if (bss->default_wep_key_len > 13) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key len %lu (= %lu bits)", line, + (unsigned long) + bss->default_wep_key_len, + (unsigned long) + bss->default_wep_key_len * 8); + errors++; + } + } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { + bss->individual_wep_key_len = atoi(pos); + if (bss->individual_wep_key_len < 0 || + bss->individual_wep_key_len > 13) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key len %d (= %d bits)", line, + bss->individual_wep_key_len, + bss->individual_wep_key_len * 8); + errors++; + } + } else if (os_strcmp(buf, "wep_rekey_period") == 0) { + bss->wep_rekeying_period = atoi(pos); + if (bss->wep_rekeying_period < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "period %d", + line, bss->wep_rekeying_period); + errors++; + } + } else if (os_strcmp(buf, "eap_reauth_period") == 0) { + bss->eap_reauth_period = atoi(pos); + if (bss->eap_reauth_period < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "period %d", + line, bss->eap_reauth_period); + errors++; + } + } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) { + bss->eapol_key_index_workaround = atoi(pos); +#ifdef CONFIG_IAPP + } else if (os_strcmp(buf, "iapp_interface") == 0) { + bss->ieee802_11f = 1; + os_strlcpy(bss->iapp_iface, pos, + sizeof(bss->iapp_iface)); +#endif /* CONFIG_IAPP */ + } else if (os_strcmp(buf, "own_ip_addr") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "nas_identifier") == 0) { + bss->nas_identifier = os_strdup(pos); + } else if (os_strcmp(buf, "auth_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &bss->radius->auth_servers, + &bss->radius->num_auth_servers, pos, 1812, + &bss->radius->auth_server)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_port") == 0) { + bss->radius->auth_server->port = atoi(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared " + "secret is not allowed.", line); + errors++; + } + bss->radius->auth_server->shared_secret = + (u8 *) os_strdup(pos); + bss->radius->auth_server->shared_secret_len = len; + } else if (os_strcmp(buf, "acct_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &bss->radius->acct_servers, + &bss->radius->num_acct_servers, pos, 1813, + &bss->radius->acct_server)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_port") == 0) { + bss->radius->acct_server->port = atoi(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared " + "secret is not allowed.", line); + errors++; + } + bss->radius->acct_server->shared_secret = + (u8 *) os_strdup(pos); + bss->radius->acct_server->shared_secret_len = len; + } else if (os_strcmp(buf, "radius_retry_primary_interval") == + 0) { + bss->radius->retry_primary_interval = atoi(pos); + } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) + { + bss->radius->acct_interim_interval = atoi(pos); + } else if (os_strcmp(buf, "auth_algs") == 0) { + bss->auth_algs = atoi(pos); + if (bss->auth_algs == 0) { + wpa_printf(MSG_ERROR, "Line %d: no " + "authentication algorithms allowed", + line); + errors++; + } + } else if (os_strcmp(buf, "max_num_sta") == 0) { + bss->max_num_sta = atoi(pos); + if (bss->max_num_sta < 0 || + bss->max_num_sta > MAX_STA_COUNT) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "max_num_sta=%d; allowed range " + "0..%d", line, bss->max_num_sta, + MAX_STA_COUNT); + errors++; + } + } else if (os_strcmp(buf, "wpa") == 0) { + bss->wpa = atoi(pos); + } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { + bss->wpa_group_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { + bss->wpa_strict_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { + bss->wpa_gmk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { + bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_passphrase") == 0) { + int len = os_strlen(pos); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: invalid WPA " + "passphrase length %d (expected " + "8..63)", line, len); + errors++; + } else { + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_strdup(pos); + } + } else if (os_strcmp(buf, "wpa_psk") == 0) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk == NULL) + errors++; + else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, + PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK " + "'%s'.", line, pos); + errors++; + } else { + bss->ssid.wpa_psk->group = 1; + } + } else if (os_strcmp(buf, "wpa_psk_file") == 0) { + os_free(bss->ssid.wpa_psk_file); + bss->ssid.wpa_psk_file = os_strdup(pos); + if (!bss->ssid.wpa_psk_file) { + wpa_printf(MSG_ERROR, "Line %d: allocation " + "failed", line); + errors++; + } + } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) { + bss->wpa_key_mgmt = + hostapd_config_parse_key_mgmt(line, pos); + if (bss->wpa_key_mgmt == -1) + errors++; + } else if (os_strcmp(buf, "wpa_pairwise") == 0) { + bss->wpa_pairwise = + hostapd_config_parse_cipher(line, pos); + if (bss->wpa_pairwise == -1 || + bss->wpa_pairwise == 0) + errors++; + else if (bss->wpa_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported " + "pairwise cipher suite '%s'", + bss->wpa_pairwise, pos); + errors++; + } + } else if (os_strcmp(buf, "rsn_pairwise") == 0) { + bss->rsn_pairwise = + hostapd_config_parse_cipher(line, pos); + if (bss->rsn_pairwise == -1 || + bss->rsn_pairwise == 0) + errors++; + else if (bss->rsn_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported " + "pairwise cipher suite '%s'", + bss->rsn_pairwise, pos); + errors++; + } +#ifdef CONFIG_RSN_PREAUTH + } else if (os_strcmp(buf, "rsn_preauth") == 0) { + bss->rsn_preauth = atoi(pos); + } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) { + bss->rsn_preauth_interfaces = os_strdup(pos); +#endif /* CONFIG_RSN_PREAUTH */ +#ifdef CONFIG_PEERKEY + } else if (os_strcmp(buf, "peerkey") == 0) { + bss->peerkey = atoi(pos); +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211R + } else if (os_strcmp(buf, "mobility_domain") == 0) { + if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || + hexstr2bin(pos, bss->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "mobility_domain '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r1_key_holder") == 0) { + if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN || + hexstr2bin(pos, bss->r1_key_holder, + FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r1_key_holder '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { + bss->r0_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "reassociation_deadline") == 0) { + bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "r0kh") == 0) { + if (add_r0kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r0kh '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r1kh") == 0) { + if (add_r1kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r1kh '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "pmk_r1_push") == 0) { + bss->pmk_r1_push = atoi(pos); +#endif /* CONFIG_IEEE80211R */ + } else if (os_strcmp(buf, "ctrl_interface") == 0) { + os_free(bss->ctrl_interface); + bss->ctrl_interface = os_strdup(pos); + } else if (os_strcmp(buf, "ctrl_interface_group") == 0) { +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + char *endp; + const char *group = pos; + + grp = getgrnam(group); + if (grp) { + bss->ctrl_interface_gid = grp->gr_gid; + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + bss->ctrl_interface_gid, group); + continue; + } + + /* Group name not found - try to parse this as gid */ + bss->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group " + "'%s'", line, group); + errors++; + continue; + } + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + bss->ctrl_interface_gid); +#endif /* CONFIG_NATIVE_WINDOWS */ +#ifdef RADIUS_SERVER + } else if (os_strcmp(buf, "radius_server_clients") == 0) { + os_free(bss->radius_server_clients); + bss->radius_server_clients = os_strdup(pos); + } else if (os_strcmp(buf, "radius_server_auth_port") == 0) { + bss->radius_server_auth_port = atoi(pos); + } else if (os_strcmp(buf, "radius_server_ipv6") == 0) { + bss->radius_server_ipv6 = atoi(pos); +#endif /* RADIUS_SERVER */ + } else if (os_strcmp(buf, "test_socket") == 0) { + os_free(bss->test_socket); + bss->test_socket = os_strdup(pos); + } else if (os_strcmp(buf, "use_pae_group_addr") == 0) { + bss->use_pae_group_addr = atoi(pos); + } else if (os_strcmp(buf, "hw_mode") == 0) { + if (os_strcmp(pos, "a") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211A; + else if (os_strcmp(pos, "b") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211B; + else if (os_strcmp(pos, "g") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + else { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "hw_mode '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "channel") == 0) { + conf->channel = atoi(pos); + } else if (os_strcmp(buf, "beacon_int") == 0) { + int val = atoi(pos); + /* MIB defines range as 1..65535, but very small values + * cause problems with the current implementation. + * Since it is unlikely that this small numbers are + * useful in real life scenarios, do not allow beacon + * period to be set below 15 TU. */ + if (val < 15 || val > 65535) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "beacon_int %d (expected " + "15..65535)", line, val); + errors++; + } else + conf->beacon_int = val; + } else if (os_strcmp(buf, "dtim_period") == 0) { + bss->dtim_period = atoi(pos); + if (bss->dtim_period < 1 || bss->dtim_period > 255) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "dtim_period %d", + line, bss->dtim_period); + errors++; + } + } else if (os_strcmp(buf, "rts_threshold") == 0) { + conf->rts_threshold = atoi(pos); + if (conf->rts_threshold < 0 || + conf->rts_threshold > 2347) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "rts_threshold %d", + line, conf->rts_threshold); + errors++; + } + } else if (os_strcmp(buf, "fragm_threshold") == 0) { + conf->fragm_threshold = atoi(pos); + if (conf->fragm_threshold < 256 || + conf->fragm_threshold > 2346) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "fragm_threshold %d", + line, conf->fragm_threshold); + errors++; + } + } else if (os_strcmp(buf, "send_probe_response") == 0) { + int val = atoi(pos); + if (val != 0 && val != 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "send_probe_response %d (expected " + "0 or 1)", line, val); + } else + conf->send_probe_response = val; + } else if (os_strcmp(buf, "supported_rates") == 0) { + if (hostapd_parse_rates(&conf->supported_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate " + "list", line); + errors++; + } + } else if (os_strcmp(buf, "basic_rates") == 0) { + if (hostapd_parse_rates(&conf->basic_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate " + "list", line); + errors++; + } + } else if (os_strcmp(buf, "preamble") == 0) { + if (atoi(pos)) + conf->preamble = SHORT_PREAMBLE; + else + conf->preamble = LONG_PREAMBLE; + } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { + bss->ignore_broadcast_ssid = atoi(pos); + } else if (os_strcmp(buf, "bridge_packets") == 0) { + conf->bridge_packets = atoi(pos); + } else if (os_strcmp(buf, "wep_default_key") == 0) { + bss->ssid.wep.idx = atoi(pos); + if (bss->ssid.wep.idx > 3) { + wpa_printf(MSG_ERROR, "Invalid " + "wep_default_key index %d", + bss->ssid.wep.idx); + errors++; + } + } else if (os_strcmp(buf, "wep_key0") == 0 || + os_strcmp(buf, "wep_key1") == 0 || + os_strcmp(buf, "wep_key2") == 0 || + os_strcmp(buf, "wep_key3") == 0) { + if (hostapd_config_read_wep(&bss->ssid.wep, + buf[7] - '0', pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key '%s'", line, buf); + errors++; + } + } else if (os_strcmp(buf, "dynamic_vlan") == 0) { + bss->ssid.dynamic_vlan = atoi(pos); + } else if (os_strcmp(buf, "vlan_file") == 0) { + if (hostapd_config_read_vlan_file(bss, pos)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "read VLAN file '%s'", line, pos); + errors++; + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { + bss->ssid.vlan_tagged_interface = os_strdup(pos); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } else if (os_strcmp(buf, "passive_scan_interval") == 0) { + conf->passive_scan_interval = atoi(pos); + } else if (os_strcmp(buf, "passive_scan_listen") == 0) { + conf->passive_scan_listen = atoi(pos); + } else if (os_strcmp(buf, "passive_scan_mode") == 0) { + conf->passive_scan_mode = atoi(pos); + } else if (os_strcmp(buf, "ap_table_max_size") == 0) { + conf->ap_table_max_size = atoi(pos); + } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) { + conf->ap_table_expiration_time = atoi(pos); + } else if (os_strncmp(buf, "tx_queue_", 9) == 0) { + if (hostapd_config_tx_queue(conf, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid TX " + "queue item", line); + errors++; + } + } else if (os_strcmp(buf, "wme_enabled") == 0) { + bss->wme_enabled = atoi(pos); + } else if (os_strncmp(buf, "wme_ac_", 7) == 0) { + if (hostapd_config_wme_ac(conf, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid wme " + "ac item", line); + errors++; + } + } else if (os_strcmp(buf, "bss") == 0) { + if (hostapd_config_bss(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bss " + "item", line); + errors++; + } + } else if (os_strcmp(buf, "bssid") == 0) { + if (bss == conf->bss && + (!conf->driver || !conf->driver->init_bssid)) { + wpa_printf(MSG_ERROR, "Line %d: bssid item " + "not allowed for the default " + "interface and this driver", line); + errors++; + } else if (hwaddr_aton(pos, bss->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bssid " + "item", line); + errors++; + } +#ifdef CONFIG_IEEE80211W + } else if (os_strcmp(buf, "ieee80211w") == 0) { + bss->ieee80211w = atoi(pos); + } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) { + bss->assoc_sa_query_max_timeout = atoi(pos); + if (bss->assoc_sa_query_max_timeout == 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "assoc_sa_query_max_timeout", line); + errors++; + } + } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) + { + bss->assoc_sa_query_retry_timeout = atoi(pos); + if (bss->assoc_sa_query_retry_timeout == 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "assoc_sa_query_retry_timeout", + line); + errors++; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211N + } else if (os_strcmp(buf, "ieee80211n") == 0) { + conf->ieee80211n = atoi(pos); + } else if (os_strcmp(buf, "ht_capab") == 0) { + if (hostapd_config_ht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "ht_capab", line); + errors++; + } +#endif /* CONFIG_IEEE80211N */ + } else if (os_strcmp(buf, "max_listen_interval") == 0) { + bss->max_listen_interval = atoi(pos); + } else if (os_strcmp(buf, "okc") == 0) { + bss->okc = atoi(pos); +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "wps_state") == 0) { + bss->wps_state = atoi(pos); + if (bss->wps_state < 0 || bss->wps_state > 2) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "wps_state", line); + errors++; + } + } else if (os_strcmp(buf, "ap_setup_locked") == 0) { + bss->ap_setup_locked = atoi(pos); + } else if (os_strcmp(buf, "uuid") == 0) { + if (uuid_str2bin(pos, bss->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", + line); + errors++; + } + } else if (os_strcmp(buf, "wps_pin_requests") == 0) { + os_free(bss->wps_pin_requests); + bss->wps_pin_requests = os_strdup(pos); + } else if (os_strcmp(buf, "device_name") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "device_name", line); + errors++; + } + os_free(bss->device_name); + bss->device_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer") == 0) { + if (os_strlen(pos) > 64) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "manufacturer", line); + errors++; + } + os_free(bss->manufacturer); + bss->manufacturer = os_strdup(pos); + } else if (os_strcmp(buf, "model_name") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "model_name", line); + errors++; + } + os_free(bss->model_name); + bss->model_name = os_strdup(pos); + } else if (os_strcmp(buf, "model_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "model_number", line); + errors++; + } + os_free(bss->model_number); + bss->model_number = os_strdup(pos); + } else if (os_strcmp(buf, "serial_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "serial_number", line); + errors++; + } + os_free(bss->serial_number); + bss->serial_number = os_strdup(pos); + } else if (os_strcmp(buf, "device_type") == 0) { + os_free(bss->device_type); + bss->device_type = os_strdup(pos); + } else if (os_strcmp(buf, "config_methods") == 0) { + os_free(bss->config_methods); + bss->config_methods = os_strdup(pos); + } else if (os_strcmp(buf, "os_version") == 0) { + if (hexstr2bin(pos, bss->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "os_version", line); + errors++; + } + } else if (os_strcmp(buf, "ap_pin") == 0) { + os_free(bss->ap_pin); + bss->ap_pin = os_strdup(pos); + } else if (os_strcmp(buf, "skip_cred_build") == 0) { + bss->skip_cred_build = atoi(pos); + } else if (os_strcmp(buf, "extra_cred") == 0) { + os_free(bss->extra_cred); + bss->extra_cred = + (u8 *) os_readfile(pos, &bss->extra_cred_len); + if (bss->extra_cred == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not " + "read Credentials from '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "wps_cred_processing") == 0) { + bss->wps_cred_processing = atoi(pos); + } else if (os_strcmp(buf, "ap_settings") == 0) { + os_free(bss->ap_settings); + bss->ap_settings = + (u8 *) os_readfile(pos, &bss->ap_settings_len); + if (bss->ap_settings == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not " + "read AP Settings from '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "upnp_iface") == 0) { + bss->upnp_iface = os_strdup(pos); + } else if (os_strcmp(buf, "friendly_name") == 0) { + os_free(bss->friendly_name); + bss->friendly_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer_url") == 0) { + os_free(bss->manufacturer_url); + bss->manufacturer_url = os_strdup(pos); + } else if (os_strcmp(buf, "model_description") == 0) { + os_free(bss->model_description); + bss->model_description = os_strdup(pos); + } else if (os_strcmp(buf, "model_url") == 0) { + os_free(bss->model_url); + bss->model_url = os_strdup(pos); + } else if (os_strcmp(buf, "upc") == 0) { + os_free(bss->upc); + bss->upc = os_strdup(pos); +#endif /* CONFIG_WPS */ + } else { + wpa_printf(MSG_ERROR, "Line %d: unknown configuration " + "item '%s'", line, buf); + errors++; + } + } + + fclose(f); + + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for broadcast + * keys */ + bss->broadcast_key_idx_min = 0; + } + + /* Select group cipher based on the enabled pairwise cipher suites */ + pairwise = 0; + if (bss->wpa & 1) + pairwise |= bss->wpa_pairwise; + if (bss->wpa & 2) { + if (bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + pairwise |= bss->rsn_pairwise; + } + if (pairwise & WPA_CIPHER_TKIP) + bss->wpa_group = WPA_CIPHER_TKIP; + else + bss->wpa_group = WPA_CIPHER_CCMP; + + for (i = 0; i < conf->num_bss; i++) { + bss = &conf->bss[i]; + + bss->radius->auth_server = bss->radius->auth_servers; + bss->radius->acct_server = bss->radius->acct_servers; + + if (bss->wpa && bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_WPA; + } else if (bss->wpa) { + bss->ssid.security_policy = SECURITY_WPA_PSK; + } else if (bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + } else if (bss->ssid.wep.keys_set) + bss->ssid.security_policy = SECURITY_STATIC_WEP; + else + bss->ssid.security_policy = SECURITY_PLAINTEXT; + } + + if (hostapd_config_check(conf)) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d errors found in configuration file " + "'%s'", errors, fname); + hostapd_config_free(conf); + conf = NULL; + } + + return conf; +} + + +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) +{ + int i; + + if (a->idx != b->idx || a->default_len != b->default_len) + return 1; + for (i = 0; i < NUM_WEP_KEYS; i++) + if (a->len[i] != b->len[i] || + os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) + return 1; + return 0; +} + + +static void hostapd_config_free_radius(struct hostapd_radius_server *servers, + int num_servers) +{ + int i; + + for (i = 0; i < num_servers; i++) { + os_free(servers[i].shared_secret); + } + os_free(servers); +} + + +static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +{ + os_free(user->identity); + os_free(user->password); + os_free(user); +} + + +static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) +{ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + os_free(keys->key[i]); + keys->key[i] = NULL; + } +} + + +static void hostapd_config_free_bss(struct hostapd_bss_config *conf) +{ + struct hostapd_wpa_psk *psk, *prev; + struct hostapd_eap_user *user, *prev_user; + + if (conf == NULL) + return; + + psk = conf->ssid.wpa_psk; + while (psk) { + prev = psk; + psk = psk->next; + os_free(prev); + } + + os_free(conf->ssid.wpa_passphrase); + os_free(conf->ssid.wpa_psk_file); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + os_free(conf->ssid.vlan_tagged_interface); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + user = conf->eap_user; + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } + + os_free(conf->dump_log_name); + os_free(conf->eap_req_id_text); + os_free(conf->accept_mac); + os_free(conf->deny_mac); + os_free(conf->nas_identifier); + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); + os_free(conf->rsn_preauth_interfaces); + os_free(conf->ctrl_interface); + os_free(conf->ca_cert); + os_free(conf->server_cert); + os_free(conf->private_key); + os_free(conf->private_key_passwd); + os_free(conf->dh_file); + os_free(conf->pac_opaque_encr_key); + os_free(conf->eap_fast_a_id); + os_free(conf->eap_fast_a_id_info); + os_free(conf->eap_sim_db); + os_free(conf->radius_server_clients); + os_free(conf->test_socket); + os_free(conf->radius); + hostapd_config_free_vlan(conf); + if (conf->ssid.dyn_vlan_keys) { + struct hostapd_ssid *ssid = &conf->ssid; + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + if (ssid->dyn_vlan_keys[i] == NULL) + continue; + hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); + os_free(ssid->dyn_vlan_keys[i]); + } + os_free(ssid->dyn_vlan_keys); + ssid->dyn_vlan_keys = NULL; + } + +#ifdef CONFIG_IEEE80211R + { + struct ft_remote_r0kh *r0kh, *r0kh_prev; + struct ft_remote_r1kh *r1kh, *r1kh_prev; + + r0kh = conf->r0kh_list; + conf->r0kh_list = NULL; + while (r0kh) { + r0kh_prev = r0kh; + r0kh = r0kh->next; + os_free(r0kh_prev); + } + + r1kh = conf->r1kh_list; + conf->r1kh_list = NULL; + while (r1kh) { + r1kh_prev = r1kh; + r1kh = r1kh->next; + os_free(r1kh_prev); + } + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_WPS + os_free(conf->wps_pin_requests); + os_free(conf->device_name); + os_free(conf->manufacturer); + os_free(conf->model_name); + os_free(conf->model_number); + os_free(conf->serial_number); + os_free(conf->device_type); + os_free(conf->config_methods); + os_free(conf->ap_pin); + os_free(conf->extra_cred); + os_free(conf->ap_settings); + os_free(conf->upnp_iface); + os_free(conf->friendly_name); + os_free(conf->manufacturer_url); + os_free(conf->model_description); + os_free(conf->model_url); + os_free(conf->upc); +#endif /* CONFIG_WPS */ +} + + +/** + * hostapd_config_free - Free hostapd configuration + * @conf: Configuration data from hostapd_config_read(). + */ +void hostapd_config_free(struct hostapd_config *conf) +{ + size_t i; + + if (conf == NULL) + return; + + for (i = 0; i < conf->num_bss; i++) + hostapd_config_free_bss(&conf->bss[i]); + os_free(conf->bss); + + os_free(conf); +} + + +/** + * hostapd_maclist_found - Find a MAC address from a list + * @list: MAC address list + * @num_entries: Number of addresses in the list + * @addr: Address to search for + * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed + * Returns: 1 if address is in the list or 0 if not. + * + * Perform a binary search for given MAC address from a pre-sorted list. + */ +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id) +{ + int start, end, middle, res; + + start = 0; + end = num_entries - 1; + + while (start <= end) { + middle = (start + end) / 2; + res = os_memcmp(list[middle].addr, addr, ETH_ALEN); + if (res == 0) { + if (vlan_id) + *vlan_id = list[middle].vlan_id; + return 1; + } + if (res < 0) + start = middle + 1; + else + end = middle - 1; + } + + return 0; +} + + +int hostapd_rate_found(int *list, int rate) +{ + int i; + + if (list == NULL) + return 0; + + for (i = 0; list[i] >= 0; i++) + if (list[i] == rate) + return 1; + + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return v->ifname; + v = v->next; + } + return NULL; +} + + +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk) +{ + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0)) + return psk->psk; + + if (psk->psk == prev_psk) + next_ok = 1; + } + + return NULL; +} + + +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && conf->ap_pin && + identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = os_strlen(conf->ap_pin); + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + + return user; +} diff --git a/contrib/hostapd/config.h b/hostapd/config.h index fafe8e041106d..a3247f26d4eeb 100644 --- a/contrib/hostapd/config.h +++ b/hostapd/config.h @@ -1,6 +1,7 @@ /* * hostapd / Configuration file - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,11 +16,24 @@ #ifndef CONFIG_H #define CONFIG_H -#include "config_types.h" +#include "defs.h" +#include "ip_addr.h" +#include "wpa_common.h" + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif typedef u8 macaddr[ETH_ALEN]; +struct mac_acl_entry { + macaddr addr; + int vlan_id; +}; + struct hostapd_radius_servers; +struct ft_remote_r0kh; +struct ft_remote_r1kh; #define HOSTAPD_MAX_SSID_LEN 32 @@ -107,6 +121,7 @@ struct hostapd_eap_user { unsigned int wildcard_prefix:1; unsigned int password_hash:1; /* whether password is hashed with * nt_password_hash() */ + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; @@ -136,28 +151,11 @@ struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; - enum { - HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, - HOSTAPD_LEVEL_DEBUG = 1, - HOSTAPD_LEVEL_INFO = 2, - HOSTAPD_LEVEL_NOTICE = 3, - HOSTAPD_LEVEL_WARNING = 4 - } logger_syslog_level, logger_stdout_level; - -#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) -#define HOSTAPD_MODULE_MLME BIT(6) + enum hostapd_logger_level logger_syslog_level, logger_stdout_level; + unsigned int logger_syslog; /* module bitfield */ unsigned int logger_stdout; /* module bitfield */ - enum { HOSTAPD_DEBUG_NO = 0, HOSTAPD_DEBUG_MINIMAL = 1, - HOSTAPD_DEBUG_VERBOSE = 2, - HOSTAPD_DEBUG_MSGDUMPS = 3, - HOSTAPD_DEBUG_EXCESSIVE = 4 } debug; /* debug verbosity level */ char *dump_log_name; /* file name for state dump (SIGUSR1) */ int max_num_sta; /* maximum number of STAs in station table */ @@ -191,52 +189,54 @@ struct hostapd_bss_config { char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast * frames */ - u8 assoc_ap_addr[ETH_ALEN]; - int assoc_ap; /* whether assoc_ap_addr is set */ - enum { ACCEPT_UNLESS_DENIED = 0, DENY_UNLESS_ACCEPTED = 1, USE_EXTERNAL_RADIUS_AUTH = 2 } macaddr_acl; - macaddr *accept_mac; + struct mac_acl_entry *accept_mac; int num_accept_mac; - macaddr *deny_mac; + struct mac_acl_entry *deny_mac; int num_deny_mac; -#define HOSTAPD_AUTH_OPEN BIT(0) -#define HOSTAPD_AUTH_SHARED_KEY BIT(1) int auth_algs; /* bitfield of allowed IEEE 802.11 authentication - * algorithms */ + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ -#define HOSTAPD_WPA_VERSION_WPA BIT(0) -#define HOSTAPD_WPA_VERSION_WPA2 BIT(1) - int wpa; -#define WPA_KEY_MGMT_IEEE8021X BIT(0) -#define WPA_KEY_MGMT_PSK BIT(1) + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ int wpa_key_mgmt; -#define WPA_CIPHER_NONE BIT(0) -#define WPA_CIPHER_WEP40 BIT(1) -#define WPA_CIPHER_WEP104 BIT(2) -#define WPA_CIPHER_TKIP BIT(3) -#define WPA_CIPHER_CCMP BIT(4) #ifdef CONFIG_IEEE80211W -#define WPA_CIPHER_AES_128_CMAC BIT(5) enum { NO_IEEE80211W = 0, IEEE80211W_OPTIONAL = 1, IEEE80211W_REQUIRED = 2 } ieee80211w; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ int wpa_pairwise; int wpa_group; int wpa_group_rekey; int wpa_strict_rekey; int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; int peerkey; +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ + char *ctrl_interface; /* directory for UNIX domain sockets */ gid_t ctrl_interface_gid; int ctrl_interface_gid_set; @@ -246,6 +246,16 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + char *dh_file; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; char *radius_server_clients; int radius_server_auth_port; @@ -266,6 +276,43 @@ struct hostapd_bss_config { struct hostapd_vlan *vlan, *vlan_tail; macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + + int okc; /* Opportunistic Key Caching */ + + int wps_state; +#ifdef CONFIG_WPS + int ap_setup_locked; + u8 uuid[16]; + char *wps_pin_requests; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + char *device_type; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; +#endif /* CONFIG_WPS */ }; @@ -282,7 +329,6 @@ typedef enum { */ struct hostapd_config { struct hostapd_bss_config *bss, *last_bss; - struct hostapd_radius_servers *radius; size_t num_bss; u16 beacon_int; @@ -305,7 +351,7 @@ struct hostapd_config { int *supported_rates; int *basic_rates; - const struct driver_ops *driver; + const struct wpa_driver_ops *driver; int passive_scan_interval; /* seconds, 0 = disabled */ int passive_scan_listen; /* usec */ @@ -321,7 +367,6 @@ struct hostapd_config { */ int ieee80211d; - unsigned int ieee80211h; /* Enable/Disable 80211h */ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; @@ -339,6 +384,13 @@ struct hostapd_config { INTERNAL_BRIDGE_DISABLED = 0, INTERNAL_BRIDGE_ENABLED = 1 } bridge_packets; + +#ifdef CONFIG_IEEE80211N + int ht_op_mode_fixed; + u16 ht_capab; +#endif /* CONFIG_IEEE80211N */ + int ieee80211n; + int secondary_channel; }; @@ -346,7 +398,8 @@ int hostapd_mac_comp(const void *a, const void *b); int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_read(const char *fname); void hostapd_config_free(struct hostapd_config *conf); -int hostapd_maclist_found(macaddr *list, int num_entries, const u8 *addr); +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id); int hostapd_rate_found(int *list, int rate); int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b); diff --git a/contrib/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9863782ed1484..fe63e7cc0451c 100644 --- a/contrib/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1,6 +1,6 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,14 +22,14 @@ #include "hostapd.h" #include "eloop.h" #include "config.h" -#include "eapol_sm.h" #include "ieee802_1x.h" #include "wpa.h" -#include "radius_client.h" +#include "radius/radius_client.h" #include "ieee802_11.h" #include "ctrl_iface.h" #include "sta_info.h" #include "accounting.h" +#include "wps_hostapd.h" struct wpa_ctrl_dst { @@ -41,16 +41,20 @@ struct wpa_ctrl_dst { }; +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + const char *buf, size_t len); + + static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; - dst = wpa_zalloc(sizeof(*dst)); + dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; - memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; dst->next = hapd->ctrl_dst; @@ -70,12 +74,13 @@ static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && - memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) == + 0) { if (prev == NULL) hapd->ctrl_dst = dst->next; else prev->next = dst->next; - free(dst); + os_free(dst); wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", (u8 *) from->sun_path, fromlen); return 0; @@ -99,7 +104,8 @@ static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && - memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) == + 0) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " "level", (u8 *) from->sun_path, fromlen); dst->debug_level = atoi(level); @@ -119,15 +125,15 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, int len, res, ret; if (sta == NULL) { - ret = snprintf(buf, buflen, "FAIL\n"); + ret = os_snprintf(buf, buflen, "FAIL\n"); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; } len = 0; - ret = snprintf(buf + len, buflen - len, MACSTR "\n", - MAC2STR(sta->addr)); + ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; @@ -161,7 +167,7 @@ static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, int ret; if (hwaddr_aton(txtaddr, addr)) { - ret = snprintf(buf, buflen, "FAIL\n"); + ret = os_snprintf(buf, buflen, "FAIL\n"); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; @@ -181,7 +187,7 @@ static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, if (hwaddr_aton(txtaddr, addr) || (sta = ap_get_sta(hapd, addr)) == NULL) { - ret = snprintf(buf, buflen, "FAIL\n"); + ret = os_snprintf(buf, buflen, "FAIL\n"); if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; @@ -212,11 +218,42 @@ static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, return -1; hostapd_new_assoc_sta(hapd, sta, 0); - accounting_sta_get_id(hapd, sta); return 0; } +#ifdef CONFIG_IEEE80211W +static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + ieee802_11_send_sa_query_req(hapd, addr, trans_id); + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WPS +static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) +{ + char *pin = os_strchr(txt, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + return hostapd_wps_add_pin(hapd, txt, pin); +} +#endif /* CONFIG_WPS */ + + static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -238,20 +275,20 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, buf[res] = '\0'; wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); - reply = malloc(reply_size); + reply = os_malloc(reply_size); if (reply == NULL) { sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, fromlen); return; } - memcpy(reply, "OK\n", 3); + os_memcpy(reply, "OK\n", 3); reply_len = 3; - if (strcmp(buf, "PING") == 0) { - memcpy(reply, "PONG\n", 5); + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); reply_len = 5; - } else if (strcmp(buf, "MIB") == 0) { + } else if (os_strcmp(buf, "MIB") == 0) { reply_len = ieee802_11_get_mib(hapd, reply, reply_size); if (reply_len >= 0) { res = wpa_get_mib(hapd->wpa_auth, reply + reply_len, @@ -278,39 +315,52 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, else reply_len += res; } - } else if (strcmp(buf, "STA-FIRST") == 0) { + } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, reply_size); - } else if (strncmp(buf, "STA ", 4) == 0) { + } else if (os_strncmp(buf, "STA ", 4) == 0) { reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, reply_size); - } else if (strncmp(buf, "STA-NEXT ", 9) == 0) { + } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); - } else if (strcmp(buf, "ATTACH") == 0) { + } else if (os_strcmp(buf, "ATTACH") == 0) { if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) reply_len = -1; - } else if (strcmp(buf, "DETACH") == 0) { + } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) reply_len = -1; - } else if (strncmp(buf, "LEVEL ", 6) == 0) { + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { if (hostapd_ctrl_iface_level(hapd, &from, fromlen, buf + 6)) reply_len = -1; - } else if (strncmp(buf, "NEW_STA ", 8) == 0) { + } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) reply_len = -1; +#ifdef CONFIG_IEEE80211W + } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { + if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) + reply_len = -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) + reply_len = -1; + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + if (hostapd_wps_button_pushed(hapd)) + reply_len = -1; +#endif /* CONFIG_WPS */ } else { - memcpy(reply, "UNKNOWN COMMAND\n", 16); + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { - memcpy(reply, "FAIL\n", 5); + os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); - free(reply); + os_free(reply); } @@ -322,19 +372,29 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) if (hapd->conf->ctrl_interface == NULL) return NULL; - len = strlen(hapd->conf->ctrl_interface) + strlen(hapd->conf->iface) + - 2; - buf = malloc(len); + len = os_strlen(hapd->conf->ctrl_interface) + + os_strlen(hapd->conf->iface) + 2; + buf = os_malloc(len); if (buf == NULL) return NULL; - snprintf(buf, len, "%s/%s", - hapd->conf->ctrl_interface, hapd->conf->iface); + os_snprintf(buf, len, "%s/%s", + hapd->conf->ctrl_interface, hapd->conf->iface); buf[len - 1] = '\0'; return buf; } +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + if (hapd == NULL) + return; + hostapd_ctrl_iface_send(hapd, level, txt, len); +} + + int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { struct sockaddr_un addr; @@ -363,8 +423,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) return -1; } - if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface) - >= sizeof(addr.sun_path)) + if (os_strlen(hapd->conf->ctrl_interface) + 1 + + os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) goto fail; s = socket(PF_UNIX, SOCK_DGRAM, 0); @@ -373,12 +433,12 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) goto fail; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; fname = hostapd_ctrl_iface_path(hapd); if (fname == NULL) goto fail; - strncpy(addr.sun_path, fname, sizeof(addr.sun_path)); + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(PF_UNIX)"); goto fail; @@ -394,11 +454,12 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) perror("chmod[ctrl_interface/ifname]"); goto fail; } - free(fname); + os_free(fname); hapd->ctrl_sock = s; eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, NULL); + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); return 0; @@ -407,7 +468,7 @@ fail: close(s); if (fname) { unlink(fname); - free(fname); + os_free(fname); } return -1; } @@ -425,7 +486,7 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) fname = hostapd_ctrl_iface_path(hapd); if (fname) unlink(fname); - free(fname); + os_free(fname); if (hapd->conf->ctrl_interface && rmdir(hapd->conf->ctrl_interface) < 0) { @@ -443,13 +504,13 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) while (dst) { prev = dst; dst = dst->next; - free(prev); + os_free(prev); } } -void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, - char *buf, size_t len) +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; struct msghdr msg; @@ -461,12 +522,12 @@ void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, if (hapd->ctrl_sock < 0 || dst == NULL) return; - snprintf(levelstr, sizeof(levelstr), "<%d>", level); + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); io[0].iov_base = levelstr; - io[0].iov_len = strlen(levelstr); - io[1].iov_base = buf; + io[0].iov_len = os_strlen(levelstr); + io[1].iov_base = (char *) buf; io[1].iov_len = len; - memset(&msg, 0, sizeof(msg)); + os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 2; diff --git a/contrib/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h index 2ac2f3b29931c..d86de8c94066d 100644 --- a/contrib/hostapd/ctrl_iface.h +++ b/hostapd/ctrl_iface.h @@ -17,7 +17,5 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); -void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, - char *buf, size_t len); #endif /* CTRL_IFACE_H */ diff --git a/contrib/hostapd/defconfig b/hostapd/defconfig index 634f7f5fce8d3..96a023d2e65d7 100644 --- a/contrib/hostapd/defconfig +++ b/hostapd/defconfig @@ -17,25 +17,28 @@ CONFIG_DRIVER_HOSTAP=y # Driver interface for madwifi driver #CONFIG_DRIVER_MADWIFI=y -#CFLAGS += -I../head # change to reflect local setup; directory for madwifi src +#CFLAGS += -I../../madwifi # change to the madwifi source directory # Driver interface for Prism54 driver #CONFIG_DRIVER_PRISM54=y -# Driver interface for drivers using Devicescape IEEE 802.11 stack -#CONFIG_DRIVER_DEVICESCAPE=y -# Currently, driver_devicescape.c build requires some additional parameters -# to be able to include some of the kernel header files. Following lines can -# be used to set these (WIRELESS_DEV must point to the root directory of the -# wireless-dev.git tree). -#WIRELESS_DEV=/usr/src/wireless-dev -#CFLAGS += -I$(WIRELESS_DEV)/net/mac80211 +# Driver interface for drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be +# shipped with your distribution yet. If that is the case, you need to build +# newer libnl version and point the hostapd build to use it. +#LIBNL=/usr/src/libnl +#CFLAGS += -I$(LIBNL)/include +#LIBS += -L$(LIBNL)/lib # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y #CFLAGS += -I/usr/local/include #LIBS += -L/usr/local/lib +# Driver interface for no driver (e.g., RADIUS server only) +#CONFIG_DRIVER_NONE=y + # IEEE 802.11F/IAPP CONFIG_IAPP=y @@ -78,6 +81,10 @@ CONFIG_EAP_TTLS=y # EAP-AKA for the integrated EAP server #CONFIG_EAP_AKA=y +# EAP-AKA' for the integrated EAP server +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + # EAP-PAX for the integrated EAP server #CONFIG_EAP_PAX=y @@ -92,6 +99,24 @@ CONFIG_EAP_TTLS=y # Include support for optional SHA256 cipher suite in EAP-GPSK #CONFIG_EAP_GPSK_SHA256=y +# EAP-FAST for the integrated EAP server +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# Wi-Fi Protected Setup (WPS) +#CONFIG_WPS=y +# Enable UPnP support for external WPS Registrars +#CONFIG_WPS_UPNP=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# Trusted Network Connect (EAP-TNC) +#CONFIG_EAP_TNC=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y @@ -103,6 +128,17 @@ CONFIG_PKCS12=y # Build IPv6 support for RADIUS operations CONFIG_IPV6=y +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + # Use the hostapd's IEEE 802.11 authentication (ACL), but without -# the IEEE 802.11 Management capability -CONFIG_DRIVER_RADIUS_ACL=y +# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) +#CONFIG_DRIVER_RADIUS_ACL=y + +# IEEE 802.11n (High Throughput) support +#CONFIG_IEEE80211N=y + +# Remove debugging code that is printing out debug messages to stdout. +# This can be used to reduce the size of the hostapd considerably if debugging +# code is not needed. +#CONFIG_NO_STDOUT_DEBUG=y diff --git a/hostapd/doc/.gitignore b/hostapd/doc/.gitignore new file mode 100644 index 0000000000000..987a5e999a140 --- /dev/null +++ b/hostapd/doc/.gitignore @@ -0,0 +1,4 @@ +html +latex +hostapd.eps +hostapd.png diff --git a/contrib/hostapd/doc/code_structure.doxygen b/hostapd/doc/code_structure.doxygen index fdcf725b5d7fb..fdcf725b5d7fb 100644 --- a/contrib/hostapd/doc/code_structure.doxygen +++ b/hostapd/doc/code_structure.doxygen diff --git a/contrib/hostapd/doc/ctrl_iface.doxygen b/hostapd/doc/ctrl_iface.doxygen index 76cfc6a6b79ce..76cfc6a6b79ce 100644 --- a/contrib/hostapd/doc/ctrl_iface.doxygen +++ b/hostapd/doc/ctrl_iface.doxygen diff --git a/contrib/hostapd/doc/doxygen.fast b/hostapd/doc/doxygen.fast index 44760f4204b3a..650c73d817f43 100644 --- a/contrib/hostapd/doc/doxygen.fast +++ b/hostapd/doc/doxygen.fast @@ -4,11 +4,10 @@ # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = hostapd -PROJECT_NUMBER = 0.5.x -OUTPUT_DIRECTORY = doc +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = hostapd/doc CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English -USE_WINDOWS_ENCODING = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ @@ -66,14 +65,14 @@ GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO +SHOW_DIRECTORIES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" @@ -81,9 +80,17 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = . \ - ../wpa_supplicant/eap_sim_common.c \ - ../wpa_supplicant/eap_sim_common.h +INPUT = hostapd \ + src/common \ + src/crypto \ + src/eap_common \ + src/eap_server \ + src/l2_packet \ + src/radius \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps FILE_PATTERNS = *.c *.h *.doxygen RECURSIVE = YES EXCLUDE = @@ -92,8 +99,8 @@ EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO -IMAGE_PATH = doc -INPUT_FILTER = doc/kerneldoc2doxygen.pl +IMAGE_PATH = hostapd/doc +INPUT_FILTER = kerneldoc2doxygen.pl FILTER_PATTERNS = FILTER_SOURCE_FILES = YES #--------------------------------------------------------------------------- @@ -220,8 +227,6 @@ 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 diff --git a/contrib/hostapd/doc/doxygen.full b/hostapd/doc/doxygen.full index 619f977303da1..f8c49bff70741 100644 --- a/contrib/hostapd/doc/doxygen.full +++ b/hostapd/doc/doxygen.full @@ -1,14 +1,13 @@ -# Doxyfile 1.4.1 +# Doxyfile 1.4.4 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = hostapd -PROJECT_NUMBER = 0.5.x -OUTPUT_DIRECTORY = doc +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = hostapd/doc CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English -USE_WINDOWS_ENCODING = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ @@ -33,6 +32,7 @@ MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES DISTRIBUTE_GROUP_DOC = NO +SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES @@ -65,14 +65,14 @@ GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO +SHOW_DIRECTORIES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" @@ -80,7 +80,17 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = . +INPUT = hostapd \ + src/common \ + src/crypto \ + src/eap_common \ + src/eap_server \ + src/l2_packet \ + src/radius \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps FILE_PATTERNS = *.c *.h *.doxygen RECURSIVE = YES EXCLUDE = @@ -89,7 +99,7 @@ EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO -IMAGE_PATH = doc +IMAGE_PATH = hostapd/doc INPUT_FILTER = kerneldoc2doxygen.pl FILTER_PATTERNS = FILTER_SOURCE_FILES = YES @@ -217,8 +227,6 @@ 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 diff --git a/contrib/hostapd/doc/driver_wrapper.doxygen b/hostapd/doc/driver_wrapper.doxygen index 0ad196f2d6b6c..0ad196f2d6b6c 100644 --- a/contrib/hostapd/doc/driver_wrapper.doxygen +++ b/hostapd/doc/driver_wrapper.doxygen diff --git a/contrib/hostapd/doc/eap.doxygen b/hostapd/doc/eap.doxygen index f0f135aa9598f..f0f135aa9598f 100644 --- a/contrib/hostapd/doc/eap.doxygen +++ b/hostapd/doc/eap.doxygen diff --git a/contrib/hostapd/doc/hostapd.fig b/hostapd/doc/hostapd.fig index af3f0be19a9b3..af3f0be19a9b3 100644 --- a/contrib/hostapd/doc/hostapd.fig +++ b/hostapd/doc/hostapd.fig diff --git a/contrib/hostapd/doc/kerneldoc2doxygen.pl b/hostapd/doc/kerneldoc2doxygen.pl index 68835a1ddd310..68835a1ddd310 100755 --- a/contrib/hostapd/doc/kerneldoc2doxygen.pl +++ b/hostapd/doc/kerneldoc2doxygen.pl diff --git a/contrib/hostapd/doc/mainpage.doxygen b/hostapd/doc/mainpage.doxygen index 7cf95de584afd..7cf95de584afd 100644 --- a/contrib/hostapd/doc/mainpage.doxygen +++ b/hostapd/doc/mainpage.doxygen diff --git a/contrib/hostapd/doc/porting.doxygen b/hostapd/doc/porting.doxygen index 0621791c0be04..0621791c0be04 100644 --- a/contrib/hostapd/doc/porting.doxygen +++ b/hostapd/doc/porting.doxygen diff --git a/contrib/hostapd/driver.h b/hostapd/driver.h index aeefbea1c45df..45f5460873028 100644 --- a/contrib/hostapd/driver.h +++ b/hostapd/driver.h @@ -1,14 +1,50 @@ +/* + * hostapd - driver interface definition + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef DRIVER_H #define DRIVER_H +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + int flags; + u16 listen_interval; + const struct ht_cap_ie *ht_capabilities; +}; + +struct hostapd_freq_params { + int mode; + int freq; + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ +}; + enum hostapd_driver_if_type { HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS }; -struct driver_ops { +struct wpa_driver_ops { const char *name; /* as appears in the config file */ - int (*init)(struct hostapd_data *hapd); + void * (*init)(struct hostapd_data *hapd); + void * (*init_bssid)(struct hostapd_data *hapd, const u8 *bssid); void (*deinit)(void *priv); int (*wireless_event_init)(void *priv); @@ -63,13 +99,18 @@ struct driver_ops { int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, int flags); int (*set_assoc_ap)(void *priv, const u8 *addr); + /* note: sta_add() is deprecated; use sta_add2() instead */ int (*sta_add)(const char *ifname, void *priv, const u8 *addr, u16 aid, u16 capability, u8 *supp_rates, size_t supp_rates_len, - int flags); + int flags, u16 listen_interval); + int (*sta_add2)(const char *ifname, void *priv, + struct hostapd_sta_add_params *params); int (*get_inact_sec)(void *priv, const u8 *addr); int (*sta_clear_stats)(void *priv, const u8 *addr); + /* note: set_freq() is deprecated; use set_freq2() instead */ int (*set_freq)(void *priv, int mode, int freq); + int (*set_freq2)(void *priv, struct hostapd_freq_params *freq); int (*set_rts)(void *priv, int rts); int (*get_rts)(void *priv, int *rts); int (*set_frag)(void *priv, int frag); @@ -78,12 +119,9 @@ struct driver_ops { int (*get_retry)(void *priv, int *short_retry, int *long_retry); int (*sta_set_flags)(void *priv, const u8 *addr, - int flags_or, int flags_and); + int total_flags, int flags_or, int flags_and); int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, int mode); - int (*set_channel_flag)(void *priv, int mode, int chan, int flag, - unsigned char power_level, - unsigned char antenna_max); int (*set_regulatory_domain)(void *priv, unsigned int rd); int (*set_country)(void *priv, const char *country); int (*set_ieee80211d)(void *priv, int enabled); @@ -142,25 +180,45 @@ struct driver_ops { */ int (*commit)(void *priv); + int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, + const u8 *data, size_t data_len); + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, - u32 session_timeout); + u32 session_timeout); int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + int (*set_ht_params)(const char *ifname, void *priv, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len); + + int (*set_wps_beacon_ie)(const char *ifname, void *priv, + const u8 *ie, size_t len); + int (*set_wps_probe_resp_ie)(const char *ifname, void *priv, + const u8 *ie, size_t len); }; -static inline int +static inline void * hostapd_driver_init(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->init == NULL) - return -1; + return NULL; return hapd->driver->init(hapd); } +static inline void * +hostapd_driver_init_bssid(struct hostapd_data *hapd, const u8 *bssid) +{ + if (hapd->driver == NULL || hapd->driver->init_bssid == NULL) + return NULL; + return hapd->driver->init_bssid(hapd, bssid); +} + static inline void hostapd_driver_deinit(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->deinit == NULL) return; - hapd->driver->deinit(hapd->driver); + hapd->driver->deinit(hapd->drv_priv); } static inline int @@ -169,7 +227,7 @@ hostapd_wireless_event_init(struct hostapd_data *hapd) if (hapd->driver == NULL || hapd->driver->wireless_event_init == NULL) return 0; - return hapd->driver->wireless_event_init(hapd->driver); + return hapd->driver->wireless_event_init(hapd->drv_priv); } static inline void @@ -178,7 +236,7 @@ hostapd_wireless_event_deinit(struct hostapd_data *hapd) if (hapd->driver == NULL || hapd->driver->wireless_event_deinit == NULL) return; - hapd->driver->wireless_event_deinit(hapd->driver); + hapd->driver->wireless_event_deinit(hapd->drv_priv); } static inline int @@ -187,7 +245,7 @@ hostapd_set_ieee8021x(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) return 0; - return hapd->driver->set_ieee8021x(ifname, hapd->driver, enabled); + return hapd->driver->set_ieee8021x(ifname, hapd->drv_priv, enabled); } static inline int @@ -195,7 +253,7 @@ hostapd_set_privacy(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) return 0; - return hapd->driver->set_privacy(hapd->conf->iface, hapd->driver, + return hapd->driver->set_privacy(hapd->conf->iface, hapd->drv_priv, enabled); } @@ -206,7 +264,7 @@ hostapd_set_encryption(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) return 0; - return hapd->driver->set_encryption(ifname, hapd->driver, alg, addr, + return hapd->driver->set_encryption(ifname, hapd->drv_priv, alg, addr, idx, key, key_len, txkey); } @@ -216,7 +274,8 @@ hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) return 0; - return hapd->driver->get_seqnum(ifname, hapd->driver, addr, idx, seq); + return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, + seq); } static inline int @@ -225,7 +284,7 @@ hostapd_get_seqnum_igtk(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->get_seqnum_igtk == NULL) return -1; - return hapd->driver->get_seqnum_igtk(ifname, hapd->driver, addr, idx, + return hapd->driver->get_seqnum_igtk(ifname, hapd->drv_priv, addr, idx, seq); } @@ -234,7 +293,7 @@ hostapd_flush(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->flush == NULL) return 0; - return hapd->driver->flush(hapd->driver); + return hapd->driver->flush(hapd->drv_priv); } static inline int @@ -243,8 +302,8 @@ hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, { if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) return 0; - return hapd->driver->set_generic_elem(hapd->conf->iface, hapd->driver, - elem, elem_len); + return hapd->driver->set_generic_elem(hapd->conf->iface, + hapd->drv_priv, elem, elem_len); } static inline int @@ -253,7 +312,7 @@ hostapd_read_sta_data(struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) return -1; - return hapd->driver->read_sta_data(hapd->driver, data, addr); + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); } static inline int @@ -262,7 +321,7 @@ hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, const u8 *data, { if (hapd->driver == NULL || hapd->driver->send_eapol == NULL) return 0; - return hapd->driver->send_eapol(hapd->driver, addr, data, data_len, + return hapd->driver->send_eapol(hapd->drv_priv, addr, data, data_len, encrypt, hapd->own_addr); } @@ -271,7 +330,7 @@ hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) return 0; - return hapd->driver->sta_deauth(hapd->driver, addr, reason); + return hapd->driver->sta_deauth(hapd->drv_priv, addr, reason); } static inline int @@ -279,7 +338,7 @@ hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) return 0; - return hapd->driver->sta_disassoc(hapd->driver, addr, reason); + return hapd->driver->sta_disassoc(hapd->drv_priv, addr, reason); } static inline int @@ -287,7 +346,7 @@ hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) return 0; - return hapd->driver->sta_remove(hapd->driver, addr); + return hapd->driver->sta_remove(hapd->drv_priv, addr); } static inline int @@ -295,7 +354,7 @@ hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) { if (hapd->driver == NULL || hapd->driver->get_ssid == NULL) return 0; - return hapd->driver->get_ssid(hapd->conf->iface, hapd->driver, buf, + return hapd->driver->get_ssid(hapd->conf->iface, hapd->drv_priv, buf, len); } @@ -304,7 +363,7 @@ hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) { if (hapd->driver == NULL || hapd->driver->set_ssid == NULL) return 0; - return hapd->driver->set_ssid(hapd->conf->iface, hapd->driver, buf, + return hapd->driver->set_ssid(hapd->conf->iface, hapd->drv_priv, buf, len); } @@ -314,7 +373,7 @@ hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len, { if (hapd->driver == NULL || hapd->driver->send_mgmt_frame == NULL) return 0; - return hapd->driver->send_mgmt_frame(hapd->driver, msg, len, flags); + return hapd->driver->send_mgmt_frame(hapd->drv_priv, msg, len, flags); } static inline int @@ -322,7 +381,7 @@ hostapd_set_assoc_ap(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL) return 0; - return hapd->driver->set_assoc_ap(hapd->driver, addr); + return hapd->driver->set_assoc_ap(hapd->drv_priv, addr); } static inline int @@ -330,19 +389,38 @@ hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) return 0; - return hapd->driver->set_countermeasures(hapd->driver, enabled); + return hapd->driver->set_countermeasures(hapd->drv_priv, enabled); } static inline int hostapd_sta_add(const char *ifname, struct hostapd_data *hapd, const u8 *addr, - u16 aid, u16 capability, u8 *supp_rates, size_t supp_rates_len, - int flags) + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, int flags, u16 listen_interval, + const struct ht_cap_ie *ht_capabilities) { - if (hapd->driver == NULL || hapd->driver->sta_add == NULL) + if (hapd->driver == NULL) + return 0; + + if (hapd->driver->sta_add2) { + struct hostapd_sta_add_params params; + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.flags = flags; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capabilities; + return hapd->driver->sta_add2(ifname, hapd->drv_priv, ¶ms); + } + + if (hapd->driver->sta_add == NULL) return 0; - return hapd->driver->sta_add(ifname, hapd->driver, addr, aid, - capability, supp_rates, supp_rates_len, - flags); + return hapd->driver->sta_add(ifname, hapd->drv_priv, addr, aid, + capability, (u8 *) supp_rates, + supp_rates_len, + flags, listen_interval); } static inline int @@ -350,15 +428,28 @@ hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) return 0; - return hapd->driver->get_inact_sec(hapd->driver, addr); + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); } static inline int -hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq) +hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, int ht_enabled, + int sec_channel_offset) { - if (hapd->driver == NULL || hapd->driver->set_freq == NULL) + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq2) { + struct hostapd_freq_params data; + os_memset(&data, 0, sizeof(data)); + data.mode = mode; + data.freq = freq; + data.ht_enabled = ht_enabled; + data.sec_channel_offset = sec_channel_offset; + return hapd->driver->set_freq2(hapd->drv_priv, &data); + } + + if (hapd->driver->set_freq == NULL) return 0; - return hapd->driver->set_freq(hapd->driver, mode, freq); + return hapd->driver->set_freq(hapd->drv_priv, mode, freq); } static inline int @@ -366,7 +457,7 @@ hostapd_set_rts(struct hostapd_data *hapd, int rts) { if (hapd->driver == NULL || hapd->driver->set_rts == NULL) return 0; - return hapd->driver->set_rts(hapd->driver, rts); + return hapd->driver->set_rts(hapd->drv_priv, rts); } static inline int @@ -374,7 +465,7 @@ hostapd_get_rts(struct hostapd_data *hapd, int *rts) { if (hapd->driver == NULL || hapd->driver->get_rts == NULL) return 0; - return hapd->driver->get_rts(hapd->driver, rts); + return hapd->driver->get_rts(hapd->drv_priv, rts); } static inline int @@ -382,7 +473,7 @@ hostapd_set_frag(struct hostapd_data *hapd, int frag) { if (hapd->driver == NULL || hapd->driver->set_frag == NULL) return 0; - return hapd->driver->set_frag(hapd->driver, frag); + return hapd->driver->set_frag(hapd->drv_priv, frag); } static inline int @@ -390,7 +481,7 @@ hostapd_get_frag(struct hostapd_data *hapd, int *frag) { if (hapd->driver == NULL || hapd->driver->get_frag == NULL) return 0; - return hapd->driver->get_frag(hapd->driver, frag); + return hapd->driver->get_frag(hapd->drv_priv, frag); } static inline int @@ -398,7 +489,8 @@ hostapd_set_retry(struct hostapd_data *hapd, int short_retry, int long_retry) { if (hapd->driver == NULL || hapd->driver->set_retry == NULL) return 0; - return hapd->driver->set_retry(hapd->driver, short_retry, long_retry); + return hapd->driver->set_retry(hapd->drv_priv, short_retry, + long_retry); } static inline int @@ -406,17 +498,18 @@ hostapd_get_retry(struct hostapd_data *hapd, int *short_retry, int *long_retry) { if (hapd->driver == NULL || hapd->driver->get_retry == NULL) return 0; - return hapd->driver->get_retry(hapd->driver, short_retry, long_retry); + return hapd->driver->get_retry(hapd->drv_priv, short_retry, + long_retry); } static inline int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, - int flags_or, int flags_and) + int total_flags, int flags_or, int flags_and) { if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) return 0; - return hapd->driver->sta_set_flags(hapd->driver, addr, flags_or, - flags_and); + return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, + flags_or, flags_and); } static inline int @@ -425,28 +518,17 @@ hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, { if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) return 0; - return hapd->driver->set_rate_sets(hapd->driver, supp_rates, + return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, basic_rates, mode); } static inline int -hostapd_set_channel_flag(struct hostapd_data *hapd, int mode, int chan, - int flag, unsigned char power_level, - unsigned char antenna_max) -{ - if (hapd->driver == NULL || hapd->driver->set_channel_flag == NULL) - return 0; - return hapd->driver->set_channel_flag(hapd->driver, mode, chan, flag, - power_level, antenna_max); -} - -static inline int hostapd_set_regulatory_domain(struct hostapd_data *hapd, unsigned int rd) { if (hapd->driver == NULL || hapd->driver->set_regulatory_domain == NULL) return 0; - return hapd->driver->set_regulatory_domain(hapd->driver, rd); + return hapd->driver->set_regulatory_domain(hapd->drv_priv, rd); } static inline int @@ -455,7 +537,7 @@ hostapd_set_country(struct hostapd_data *hapd, const char *country) if (hapd->driver == NULL || hapd->driver->set_country == NULL) return 0; - return hapd->driver->set_country(hapd->driver, country); + return hapd->driver->set_country(hapd->drv_priv, country); } static inline int @@ -464,19 +546,15 @@ hostapd_set_ieee80211d(struct hostapd_data *hapd, int enabled) if (hapd->driver == NULL || hapd->driver->set_ieee80211d == NULL) return 0; - return hapd->driver->set_ieee80211d(hapd->driver, enabled); + return hapd->driver->set_ieee80211d(hapd->drv_priv, enabled); } -void driver_register(const char *name, const struct driver_ops *ops); -void driver_unregister(const char *name); -const struct driver_ops *driver_lookup(const char *name); - static inline int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) return 0; - return hapd->driver->sta_clear_stats(hapd->driver, addr); + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); } static inline int @@ -486,7 +564,7 @@ hostapd_set_beacon(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) return 0; - return hapd->driver->set_beacon(ifname, hapd->driver, head, head_len, + return hapd->driver->set_beacon(ifname, hapd->drv_priv, head, head_len, tail, tail_len); } @@ -495,7 +573,7 @@ hostapd_set_internal_bridge(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_internal_bridge == NULL) return 0; - return hapd->driver->set_internal_bridge(hapd->driver, value); + return hapd->driver->set_internal_bridge(hapd->drv_priv, value); } static inline int @@ -503,7 +581,7 @@ hostapd_set_beacon_int(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_beacon_int == NULL) return 0; - return hapd->driver->set_beacon_int(hapd->driver, value); + return hapd->driver->set_beacon_int(hapd->drv_priv, value); } static inline int @@ -511,7 +589,7 @@ hostapd_set_dtim_period(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_dtim_period == NULL) return 0; - return hapd->driver->set_dtim_period(hapd->conf->iface, hapd->driver, + return hapd->driver->set_dtim_period(hapd->conf->iface, hapd->drv_priv, value); } @@ -520,7 +598,7 @@ hostapd_set_broadcast_ssid(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_broadcast_ssid == NULL) return 0; - return hapd->driver->set_broadcast_ssid(hapd->driver, value); + return hapd->driver->set_broadcast_ssid(hapd->drv_priv, value); } static inline int @@ -528,7 +606,7 @@ hostapd_set_cts_protect(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) return 0; - return hapd->driver->set_cts_protect(hapd->driver, value); + return hapd->driver->set_cts_protect(hapd->drv_priv, value); } static inline int @@ -537,7 +615,7 @@ hostapd_set_key_tx_rx_threshold(struct hostapd_data *hapd, int value) if (hapd->driver == NULL || hapd->driver->set_key_tx_rx_threshold == NULL) return 0; - return hapd->driver->set_key_tx_rx_threshold(hapd->driver, value); + return hapd->driver->set_key_tx_rx_threshold(hapd->drv_priv, value); } static inline int @@ -545,7 +623,7 @@ hostapd_set_preamble(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) return 0; - return hapd->driver->set_preamble(hapd->driver, value); + return hapd->driver->set_preamble(hapd->drv_priv, value); } static inline int @@ -553,7 +631,7 @@ hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) { if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) return 0; - return hapd->driver->set_short_slot_time(hapd->driver, value); + return hapd->driver->set_short_slot_time(hapd->drv_priv, value); } static inline int @@ -562,7 +640,7 @@ hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, { if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) return 0; - return hapd->driver->set_tx_queue_params(hapd->driver, queue, aifs, + return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, cw_min, cw_max, burst_time); } @@ -571,7 +649,7 @@ hostapd_bss_add(struct hostapd_data *hapd, const char *ifname, const u8 *bssid) { if (hapd->driver == NULL || hapd->driver->bss_add == NULL) return 0; - return hapd->driver->bss_add(hapd->driver, ifname, bssid); + return hapd->driver->bss_add(hapd->drv_priv, ifname, bssid); } static inline int @@ -579,7 +657,7 @@ hostapd_bss_remove(struct hostapd_data *hapd, const char *ifname) { if (hapd->driver == NULL || hapd->driver->bss_remove == NULL) return 0; - return hapd->driver->bss_remove(hapd->driver, ifname); + return hapd->driver->bss_remove(hapd->drv_priv, ifname); } static inline int @@ -588,7 +666,7 @@ hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, { if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) return 1; - return hapd->driver->valid_bss_mask(hapd->driver, addr, mask); + return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); } static inline int @@ -597,7 +675,7 @@ hostapd_if_add(struct hostapd_data *hapd, enum hostapd_driver_if_type type, { if (hapd->driver == NULL || hapd->driver->if_add == NULL) return -1; - return hapd->driver->if_add(hapd->conf->iface, hapd->driver, type, + return hapd->driver->if_add(hapd->conf->iface, hapd->drv_priv, type, ifname, addr); } @@ -607,7 +685,7 @@ hostapd_if_update(struct hostapd_data *hapd, enum hostapd_driver_if_type type, { if (hapd->driver == NULL || hapd->driver->if_update == NULL) return -1; - return hapd->driver->if_update(hapd->driver, type, ifname, addr); + return hapd->driver->if_update(hapd->drv_priv, type, ifname, addr); } static inline int @@ -616,7 +694,7 @@ hostapd_if_remove(struct hostapd_data *hapd, enum hostapd_driver_if_type type, { if (hapd->driver == NULL || hapd->driver->if_remove == NULL) return -1; - return hapd->driver->if_remove(hapd->driver, type, ifname, addr); + return hapd->driver->if_remove(hapd->drv_priv, type, ifname, addr); } static inline int @@ -626,7 +704,7 @@ hostapd_passive_scan(struct hostapd_data *hapd, int now, int our_mode_only, { if (hapd->driver == NULL || hapd->driver->passive_scan == NULL) return -1; - return hapd->driver->passive_scan(hapd->driver, now, our_mode_only, + return hapd->driver->passive_scan(hapd->drv_priv, now, our_mode_only, interval, _listen, channel, last_rx); } @@ -636,7 +714,7 @@ hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, { if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) return NULL; - return hapd->driver->get_hw_feature_data(hapd->driver, num_modes, + return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, flags); } @@ -646,7 +724,7 @@ hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) return 0; - return hapd->driver->set_sta_vlan(hapd->driver, addr, ifname, vlan_id); + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id); } static inline int @@ -654,25 +732,67 @@ hostapd_driver_commit(struct hostapd_data *hapd) { if (hapd->driver == NULL || hapd->driver->commit == NULL) return 0; - return hapd->driver->commit(hapd->driver); + return hapd->driver->commit(hapd->drv_priv); } -static inline int -hostapd_set_radius_acl_auth(struct hostapd_data *hapd, const u8 *mac, int accepted, - u32 session_timeout) +static inline int +hostapd_set_radius_acl_auth(struct hostapd_data *hapd, const u8 *mac, + int accepted, u32 session_timeout) { if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) return 0; - return hapd->driver->set_radius_acl_auth(hapd->driver, mac, accepted, + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, session_timeout); } -static inline int +static inline int hostapd_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac) { - if (hapd->driver == NULL || hapd->driver->set_radius_acl_expire == NULL) + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + +#ifdef CONFIG_IEEE80211N +static inline int +hostapd_set_ht_params(const char *ifname, struct hostapd_data *hapd, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len) +{ + if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || + ht_capab == NULL || ht_oper == NULL) + return 0; + return hapd->driver->set_ht_params( + ifname, hapd->drv_priv, ht_capab, ht_capab_len, + ht_oper, ht_oper_len); +} +#endif /* CONFIG_IEEE80211N */ + +static inline int +hostapd_drv_none(struct hostapd_data *hapd) +{ + return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; +} + +static inline int +hostapd_set_wps_beacon_ie(struct hostapd_data *hapd, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->set_wps_beacon_ie == NULL) + return 0; + return hapd->driver->set_wps_beacon_ie(hapd->conf->iface, + hapd->drv_priv, ie, len); +} + +static inline int +hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie, + size_t len) +{ + if (hapd->driver == NULL || + hapd->driver->set_wps_probe_resp_ie == NULL) return 0; - return hapd->driver->set_radius_acl_expire(hapd->driver, mac); + return hapd->driver->set_wps_probe_resp_ie(hapd->conf->iface, + hapd->drv_priv, ie, len); } #endif /* DRIVER_H */ diff --git a/hostapd/driver_bsd.c b/hostapd/driver_bsd.c new file mode 100644 index 0000000000000..14c366276c952 --- /dev/null +++ b/hostapd/driver_bsd.c @@ -0,0 +1,838 @@ +/* + * hostapd / Driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, 2Wire, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include <net/if.h> + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from net80211 header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "l2_packet/l2_packet.h" + +#include "eapol_sm.h" +#include "wpa.h" +#include "radius/radius.h" +#include "ieee802_11.h" +#include "common.h" + +struct bsd_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ +}; + +static int bsd_sta_deauth(void *priv, const u8 *addr, int reason_code); + +static int +set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = (void *) arg; + + if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { + perror("ioctl[SIOCS80211]"); + return -1; + } + return 0; +} + +static int +get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = arg; + + if (ioctl(drv->ioctl_sock, SIOCG80211, &ireq) < 0) { + perror("ioctl[SIOCG80211]"); + return -1; + } + return ireq.i_len; +} + +static int +set80211param(struct bsd_driver_data *drv, int op, int arg) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_val = arg; + + if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { + perror("ioctl[SIOCS80211]"); + return -1; + } + return 0; +} + +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} + +/* + * Configure WPA parameters. + */ +static int +bsd_configure_wpa(struct bsd_driver_data *drv) +{ + static const char *ciphernames[] = + { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + int v; + + switch (conf->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + printf("Unknown group key cipher %u\n", + conf->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", + __func__, ciphernames[v], v); + if (set80211param(drv, IEEE80211_IOC_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u (%s)\n", + v, ciphernames[v]); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_IOC_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (conf->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_IOC_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, conf->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_IOC_KEYMGTALGS, conf->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + conf->wpa_key_mgmt); + return -1; + } + + v = 0; + if (conf->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, conf->rsn_preauth); + if (set80211param(drv, IEEE80211_IOC_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, conf->wpa); + if (set80211param(drv, IEEE80211_IOC_WPA, conf->wpa)) { + printf("Unable to set WPA to %u\n", conf->wpa); + return -1; + } + return 0; +} + + +static int +bsd_set_iface_flags(void *priv, int dev_up) +{ + struct bsd_driver_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + +static int +bsd_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct bsd_driver_data *drv = priv; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + if (!enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_IOC_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!conf->wpa && !conf->ieee802_1x) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (conf->wpa && bsd_configure_wpa(drv) != 0) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_IOC_AUTHMODE, + (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + return bsd_set_iface_flags(priv, 1); +} + +static int +bsd_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct bsd_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_IOC_PRIVACY, enabled); +} + +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, + int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return bsd_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WLAN_STA_AUTHORIZED)) + return bsd_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +bsd_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_del_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ + } else { + wk.idk_keyix = key_idx; + } + + return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +bsd_set_key(const char *ifname, void *priv, const char *alg, + const u8 *addr, int key_idx, + const u8 *key, size_t key_len, int txkey) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + + if (strcmp(alg, "none") == 0) + return bsd_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (strcmp(alg, "WEP") == 0) + cipher = IEEE80211_CIPHER_WEP; + else if (strcmp(alg, "TKIP") == 0) + cipher = IEEE80211_CIPHER_TKIP; + else if (strcmp(alg, "CCMP") == 0) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %s\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %d too big\n", __func__, key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + + +static int +bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (get80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { + printf("Failed to get encryption.\n"); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +bsd_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return bsd_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(drv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) > 0) { + /* XXX? do packets counts include non-data frames? */ + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + } + return 0; +} + +static int +bsd_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +bsd_sta_deauth(void *priv, const u8 *addr, int reason_code) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_sta_disassoc(void *priv, const u8 *addr, int reason_code) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_del_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + if (conf->wpa) + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); + } + return 0; +} + +static int +bsd_new_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + struct sta_info *sta; + struct ieee80211req_wpaie ie; + int new_assoc, ielen, res; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + /* + * Fetch and validate any negotiated WPA/RSN parameters. + */ + if (conf->wpa) { + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(drv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { + printf("Failed to get WPA/RSN information element.\n"); + return -1; /* XXX not right */ + } + ielen = ie.wpa_ie[1]; + if (ielen == 0) { + printf("No WPA/RSN information element for station!\n"); + return -1; /* XXX not right */ + } + ielen += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie.wpa_ie, ielen, NULL, 0); + if (res != WPA_IE_OK) { + printf("WPA/RSN information element rejected? " + "(res %u)\n", res); + return -1; + } + } + + /* + * Now that the internal station state is setup + * kick the authenticator into action. + */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + return 0; +} + +#include <net/route.h> +#include <net80211/ieee80211_freebsd.h> + +static void +bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct bsd_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + struct ieee80211_michael_event *mic; + struct ieee80211_join_event *join; + struct ieee80211_leave_event *leave; + int n; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + ifan = (struct if_announcemsghdr *) rtm; + switch (rtm->rtm_type) { + case RTM_IEEE80211: + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + case RTM_IEEE80211_DISASSOC: + case RTM_IEEE80211_SCAN: + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + bsd_del_sta(drv, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + ieee80211_michael_mic_failure(hapd, mic->iev_src, 1); + break; + } + break; + } +} + +static int +bsd_wireless_event_init(void *priv) +{ + struct bsd_driver_data *drv = priv; + int s; + + drv->wext_sock = -1; + + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + return -1; + } + eloop_register_read_sock(s, bsd_wireless_event_receive, drv, NULL); + drv->wext_sock = s; + + return 0; +} + +static void +bsd_wireless_event_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + if (drv != NULL) { + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); + } +} + + +static int +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct bsd_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Etherent header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %u!\n", len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = htons(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + sta = ap_get_sta(hapd, src_addr); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA %s\n", + ether_sprintf(src_addr)); + /* XXX cannot happen */ + return; + } + ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static int +bsd_get_ssid(const char *ifname, void *priv, u8 *buf, int len) +{ + struct bsd_driver_data *drv = priv; + int ssid_len = get80211var(drv, IEEE80211_IOC_SSID, buf, len); + + wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, ssid_len, buf); + + return ssid_len; +} + +static int +bsd_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) +{ + struct bsd_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, len, buf); + + return set80211var(drv, IEEE80211_IOC_SSID, buf, len); +} + +static void * +bsd_init(struct hostapd_data *hapd) +{ + struct bsd_driver_data *drv; + + drv = os_zalloc(sizeof(struct bsd_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for bsd driver data\n"); + goto bad; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) + goto bad; + + bsd_set_iface_flags(drv, 0); /* mark down during setup */ + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +bsd_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + (void) bsd_set_iface_flags(drv, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + free(drv); +} + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .init = bsd_init, + .deinit = bsd_deinit, + .set_ieee8021x = bsd_set_ieee8021x, + .set_privacy = bsd_set_privacy, + .set_encryption = bsd_set_key, + .get_seqnum = bsd_get_seqnum, + .flush = bsd_flush, + .set_generic_elem = bsd_set_opt_ie, + .wireless_event_init = bsd_wireless_event_init, + .wireless_event_deinit = bsd_wireless_event_deinit, + .sta_set_flags = bsd_sta_set_flags, + .read_sta_data = bsd_read_sta_driver_data, + .send_eapol = bsd_send_eapol, + .sta_disassoc = bsd_sta_disassoc, + .sta_deauth = bsd_sta_deauth, + .set_ssid = bsd_set_ssid, + .get_ssid = bsd_get_ssid, +}; diff --git a/hostapd/driver_hostap.c b/hostapd/driver_hostap.c new file mode 100644 index 0000000000000..ceff0998919c9 --- /dev/null +++ b/hostapd/driver_hostap.c @@ -0,0 +1,1279 @@ +/* + * hostapd / Kernel driver communication with Linux Host AP driver + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#ifdef USE_KERNEL_HEADERS +/* compat-wireless does not include linux/compiler.h to define __user, so + * define it here */ +#ifndef __user +#define __user +#endif /* __user */ +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/wireless.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "hostap_common.h" +#include "hw_features.h" + + +struct hostap_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for driver access */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + + int we_version; + + u8 *generic_ie; + size_t generic_ie_len; + u8 *wps_ie; + size_t wps_ie_len; +}; + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len); +static int hostap_set_iface_flags(void *priv, int dev_up); + +static void handle_data(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype) +{ + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + struct sta_info *sta; + + if (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; + } + + sa = hdr->addr2; + sta = ap_get_sta(hapd, sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA " MACSTR "\n", + MAC2STR(sa)); + if (sta && (sta->flags & WLAN_STA_AUTH)) + hostapd_sta_disassoc( + hapd, sa, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hostapd_sta_deauth( + hapd, sa, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + return; + } + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + if (left < sizeof(rfc1042_header)) { + printf("Too short data frame\n"); + return; + } + + if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { + printf("Data frame with no RFC1042 header\n"); + return; + } + pos += sizeof(rfc1042_header); + left -= sizeof(rfc1042_header); + + if (left < 2) { + printf("No ethertype in data frame\n"); + return; + } + + ethertype = WPA_GET_BE16(pos); + pos += 2; + left -= 2; + switch (ethertype) { + case ETH_P_PAE: + ieee802_1x_receive(hapd, sa, pos, left); + break; + + default: + printf("Unknown ethertype 0x%04x in data frame\n", ethertype); + break; + } +} + + +static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, + int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_MGMT: + wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", + ok ? "ACK" : "fail"); + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", + ok ? "ACK" : "fail"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA (TX callback) %s", + ok ? "ACK" : "fail"); + sta = ap_get_sta(hapd, hdr->addr1); + if (sta && sta->flags & WLAN_STA_PENDING_POLL) { + wpa_printf(MSG_DEBUG, "STA " MACSTR + " %s pending activity poll", + MAC2STR(sta->addr), + ok ? "ACKed" : "did not ACK"); + if (ok) + sta->flags &= ~WLAN_STA_PENDING_POLL; + } + if (sta) + ieee802_1x_tx_status(hapd, sta, buf, len, ok); + break; + default: + printf("unknown TX callback frame type %d\n", type); + break; + } +} + + +static void handle_frame(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, extra_len, type, stype; + unsigned char *extra = NULL; + size_t data_len = len; + int ver; + + /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass + * these to user space */ + if (len < 24) { + wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { + wpa_hexdump(MSG_MSGDUMP, "Received management frame", + buf, len); + } + + ver = fc & WLAN_FC_PVER; + + /* protocol version 3 is reserved for indicating extra data after the + * payload, version 2 for indicating ACKed frame (TX callbacks), and + * version 1 for indicating failed frame (no ACK, TX callbacks) */ + if (ver == 3) { + u8 *pos = buf + len - 2; + extra_len = WPA_GET_LE16(pos); + printf("extra data in frame (elen=%d)\n", extra_len); + if ((size_t) extra_len + 2 > len) { + printf(" extra data overflow\n"); + return; + } + len -= extra_len + 2; + extra = buf + len; + } else if (ver == 1 || ver == 2) { + handle_tx_callback(hapd, buf, data_len, ver == 2 ? 1 : 0); + return; + } else if (ver != 0) { + printf("unknown protocol version %d\n", ver); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + if (stype != WLAN_FC_STYPE_BEACON) + wpa_printf(MSG_MSGDUMP, "MGMT"); + ieee802_11_mgmt(hapd, buf, data_len, stype, NULL); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA"); + handle_data(hapd, buf, data_len, stype); + break; + default: + wpa_printf(MSG_DEBUG, "unknown frame type %d", type); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_frame(hapd, buf, len); +} + + +static int hostap_init_sockets(struct hostap_driver_data *drv) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv->hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + if (hostap_set_iface_flags(drv, 1)) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +} + + +static int hostap_send_mgmt_frame(void *priv, const void *msg, size_t len, + int flags) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; + int res; + + /* Request TX callback */ + hdr->frame_control |= host_to_le16(BIT(1)); + res = send(drv->sock, msg, len, flags); + hdr->frame_control &= ~host_to_le16(BIT(1)); + + return res; +} + + +static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for hostapd_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + pos = (u8 *) (hdr + 1); + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + *((u16 *) pos) = htons(ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = hostap_send_mgmt_frame(drv, (u8 *) hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("hostapd_send_eapol: send"); + printf("hostapd_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static int hostap_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.set_flags_sta.flags_or = flags_or; + param.u.set_flags_sta.flags_and = flags_and; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_set_iface_flags(void *priv, int dev_up) +{ + struct hostap_driver_data *drv = priv; + struct ifreq ifr; + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return -1; + } + + return 0; +} + + +static int hostap_set_encryption(const char *ifname, void *priv, + const char *alg, const u8 *addr, + int idx, const u8 *key, size_t key_len, + int txkey) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + os_strlcpy((char *) param->u.crypt.alg, alg, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = txkey ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = idx; + param->u.crypt.key_len = key_len; + memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to set encryption.\n"); + ret = -1; + } + free(buf); + + return ret; +} + + +static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + 32; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_GET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); + ret = -1; + } else { + memcpy(seq, param->u.crypt.seq, 8); + } + free(buf); + + return ret; +} + + +static int hostap_ioctl_prism2param(void *priv, int param, int value) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + int *i; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + return -1; + } + + return 0; +} + + +static int hostap_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct hostap_driver_data *drv = priv; + + /* enable kernel driver support for IEEE 802.1X */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { + printf("Could not setup IEEE 802.1X support in kernel driver." + "\n"); + return -1; + } + + if (!enabled) + return 0; + + /* use host driver implementation of encryption to allow + * individual keys and passing plaintext EAPOL frames */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || + hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { + printf("Could not setup host-based encryption in kernel " + "driver.\n"); + return -1; + } + + return 0; +} + + +static int hostap_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); +} + + +static int hostap_set_ssid(const char *ifname, void *priv, const u8 *buf, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + + return 0; +} + + +static int hostap_flush(void *priv) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_FLUSH; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) + return -1; + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +} + + +static int hostap_sta_add(const char *ifname, void *priv, const u8 *addr, + u16 aid, u16 capability, u8 *supp_rates, + size_t supp_rates_len, int flags, + u16 listen_interval) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + int tx_supp_rates = 0; + size_t i; + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) + + for (i = 0; i < supp_rates_len; i++) { + if ((supp_rates[i] & 0x7f) == 2) + tx_supp_rates |= WLAN_RATE_1M; + if ((supp_rates[i] & 0x7f) == 4) + tx_supp_rates |= WLAN_RATE_2M; + if ((supp_rates[i] & 0x7f) == 11) + tx_supp_rates |= WLAN_RATE_5M5; + if ((supp_rates[i] & 0x7f) == 22) + tx_supp_rates |= WLAN_RATE_11M; + } + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_ADD_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.add_sta.aid = aid; + param.u.add_sta.capability = capability; + param.u.add_sta.tx_supp_rates = tx_supp_rates; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_sta_remove(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WLAN_STA_AUTHORIZED); + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + printf("Could not remove station from kernel driver.\n"); + return -1; + } + return 0; +} + + +static int hostap_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return param.u.get_info_sta.inactive_sec; +} + + +static int hostap_sta_clear_stats(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return 0; +} + + +static int hostap_set_assoc_ap(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) + return -1; + + return 0; +} + + +static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen, elem_len; + + elem_len = drv->generic_ie_len + drv->wps_ie_len; + blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = elem_len; + if (drv->generic_ie) { + os_memcpy(param->u.generic_elem.data, drv->generic_ie, + drv->generic_ie_len); + } + if (drv->wps_ie) { + os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], + drv->wps_ie, drv->wps_ie_len); + } + wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", + param->u.generic_elem.data, elem_len); + res = hostapd_ioctl(drv, param, blen); + + os_free(param); + + return res; +} + + +static int hostap_set_generic_elem(const char *ifname, void *priv, + const u8 *elem, size_t elem_len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->generic_ie); + drv->generic_ie = NULL; + drv->generic_ie_len = 0; + if (elem) { + drv->generic_ie = os_malloc(elem_len); + if (drv->generic_ie == NULL) + return -1; + os_memcpy(drv->generic_ie, elem, elem_len); + drv->generic_ie_len = elem_len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static int hostap_set_wps_beacon_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + /* Host AP driver supports only one set of extra IEs, so we need to + * use the ProbeResp IEs also for Beacon frames since they include more + * information. */ + return 0; +} + + +static int hostap_set_wps_probe_resp_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->wps_ie); + drv->wps_ie = NULL; + drv->wps_ie_len = 0; + if (ie) { + drv->wps_ie = os_malloc(len); + if (drv->wps_ie == NULL) + return -1; + os_memcpy(drv->wps_ie, ie, len); + drv->wps_ie_len = len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static void +hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(struct hostap_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void hostapd_wireless_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostap_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + hostapd_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int hostap_get_we_version(struct hostap_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int hostap_wireless_event_init(void *priv) +{ + struct hostap_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + hostap_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, + NULL); + drv->wext_sock = s; + + return 0; +} + + +static void hostap_wireless_event_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); +} + + +static void * hostap_init(struct hostapd_data *hapd) +{ + struct hostap_driver_data *drv; + + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = drv->sock = -1; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + free(drv); + return NULL; + } + + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { + printf("Could not enable hostapd mode for interface %s\n", + drv->iface); + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + if (hostap_init_sockets(drv)) { + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + return drv; +} + + +static void hostap_driver_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + + (void) hostap_set_iface_flags(drv, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); + + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + if (drv->sock >= 0) + close(drv->sock); + + os_free(drv->generic_ie); + os_free(drv->wps_ie); + + free(drv); +} + + +static int hostap_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0); +} + + +static int hostap_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0); +} + + +static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags) +{ + struct hostapd_hw_modes *mode; + int i, clen, rlen; + const short chan2freq[14] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + + mode = os_zalloc(sizeof(struct hostapd_hw_modes)); + if (mode == NULL) + return NULL; + + *num_modes = 1; + *flags = 0; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + mode->num_channels = 14; + mode->num_rates = 4; + + clen = mode->num_channels * sizeof(struct hostapd_channel_data); + rlen = mode->num_rates * sizeof(struct hostapd_rate_data); + + mode->channels = os_zalloc(clen); + mode->rates = os_zalloc(rlen); + if (mode->channels == NULL || mode->rates == NULL) { + hostapd_free_hw_features(mode, *num_modes); + return NULL; + } + + for (i = 0; i < 14; i++) { + mode->channels[i].chan = i + 1; + mode->channels[i].freq = chan2freq[i]; + /* TODO: Get allowed channel list from the driver */ + if (i >= 11) + mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; + } + + mode->rates[0].rate = 10; + mode->rates[0].flags = HOSTAPD_RATE_CCK; + mode->rates[1].rate = 20; + mode->rates[1].flags = HOSTAPD_RATE_CCK; + mode->rates[2].rate = 55; + mode->rates[2].flags = HOSTAPD_RATE_CCK; + mode->rates[3].rate = 110; + mode->rates[3].flags = HOSTAPD_RATE_CCK; + + return mode; +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .init = hostap_init, + .deinit = hostap_driver_deinit, + .wireless_event_init = hostap_wireless_event_init, + .wireless_event_deinit = hostap_wireless_event_deinit, + .set_ieee8021x = hostap_set_ieee8021x, + .set_privacy = hostap_set_privacy, + .set_encryption = hostap_set_encryption, + .get_seqnum = hostap_get_seqnum, + .flush = hostap_flush, + .set_generic_elem = hostap_set_generic_elem, + .read_sta_data = hostap_read_sta_data, + .send_eapol = hostap_send_eapol, + .sta_set_flags = hostap_sta_set_flags, + .sta_deauth = hostap_sta_deauth, + .sta_disassoc = hostap_sta_disassoc, + .sta_remove = hostap_sta_remove, + .set_ssid = hostap_set_ssid, + .send_mgmt_frame = hostap_send_mgmt_frame, + .set_assoc_ap = hostap_set_assoc_ap, + .sta_add = hostap_sta_add, + .get_inact_sec = hostap_get_inact_sec, + .sta_clear_stats = hostap_sta_clear_stats, + .get_hw_feature_data = hostap_get_hw_feature_data, + .set_wps_beacon_ie = hostap_set_wps_beacon_ie, + .set_wps_probe_resp_ie = hostap_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_madwifi.c b/hostapd/driver_madwifi.c new file mode 100644 index 0000000000000..ae850b571fe30 --- /dev/null +++ b/hostapd/driver_madwifi.c @@ -0,0 +1,1483 @@ +/* + * hostapd / Driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include <include/compat.h> +#include <net80211/ieee80211.h> +#ifdef WME_NUM_AC +/* Assume this is built against BSD branch of madwifi driver. */ +#define MADWIFI_BSD +#include <net80211/_ieee80211.h> +#endif /* WME_NUM_AC */ +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from madwifi header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE +#undef WME_OUI_TYPE + + +#ifdef IEEE80211_IOCTL_SETWMMPARAMS +/* Assume this is built against madwifi-ng */ +#define MADWIFI_NG +#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +#include "wireless_copy.h" + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "sta_info.h" +#include "l2_packet/l2_packet.h" + +#include "wpa.h" +#include "radius/radius.h" +#include "ieee802_11.h" +#include "accounting.h" +#include "common.h" +#include "wps_hostapd.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code); + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); +#ifdef IEEE80211_IOCTL_FILTERFRAME + /* FILTERFRAME must be NOT inline, regardless of size. */ + if (op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF) + do_inline = 0; + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + "ioctl[IEEE80211_IOCTL_FILTERFRAME]", + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[SIOCIWFIRSTPRIV+3]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + "ioctl[SIOCIWFIRSTPRIV+5]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + "ioctl[SIOCIWFIRSTPRIV+7]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + "ioctl[SIOCIWFIRSTPRIV+11]", + "ioctl[IEEE80211_IOCTL_DELMAC]", + "ioctl[SIOCIWFIRSTPRIV+13]", + "ioctl[IEEE80211_IOCTL_CHANLIST]", + "ioctl[SIOCIWFIRSTPRIV+15]", + "ioctl[IEEE80211_IOCTL_GETRSN]", + "ioctl[SIOCIWFIRSTPRIV+17]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && + opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " + "arg %d)", __func__, op, arg); + return -1; + } + return 0; +} + +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + int v; + + switch (conf->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + conf->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (conf->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, conf->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + conf->wpa_key_mgmt); + return -1; + } + + v = 0; + if (conf->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, conf->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, conf->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { + printf("Unable to set WPA to %u\n", conf->wpa); + return -1; + } + return 0; +} + + +static int +madwifi_set_iface_flags(void *priv, int dev_up) +{ + struct madwifi_driver_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + +static int +madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + if (!enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!conf->wpa && !conf->ieee802_1x) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (conf->wpa && madwifi_configure_wpa(drv) != 0) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WLAN_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +madwifi_set_key(const char *ifname, void *priv, const char *alg, + const u8 *addr, int key_idx, + const u8 *key, size_t key_len, int txkey) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (strcmp(alg, "none") == 0) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (strcmp(alg, "WEP") == 0) + cipher = IEEE80211_CIPHER_WEP; + else if (strcmp(alg, "TKIP") == 0) + cipher = IEEE80211_CIPHER_TKIP; + else if (strcmp(alg, "CCMP") == 0) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %s\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg '%s' key_len %lu txkey %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, txkey); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ +#ifdef MADWIFI_BSD + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); +#else /* MADWIFI_BSD */ + return 0; /* XXX */ +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + +#ifdef MADWIFI_BSD + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, +#ifdef MADWIFI_NG + IEEE80211_IOCTL_STA_STATS, +#else /* MADWIFI_NG */ + IEEE80211_IOCTL_GETSTASTATS, +#endif /* MADWIFI_NG */ + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; + +#else /* MADWIFI_BSD */ + + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) { + if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) + return -1; + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ +#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ + return 0; /* FIX */ +#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ +} + + +static int +madwifi_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie; + u16 fc; + size_t ie_len; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + end = buf + len; + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); +} +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + return ret; +} + +static int +madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); + } + return 0; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); +} + +static int +madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_wps_beacon_ie NULL +#define madwifi_set_wps_probe_resp_ie NULL +#endif /* CONFIG_WPS */ + +static int +madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen, res; + u8 *iebuf; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, sta->addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + wpa_printf(MSG_ERROR, "%s: Failed to get WPA/RSN IE", + __func__); + printf("Failed to get WPA/RSN information element.\n"); + return -1; /* XXX not right */ + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; +#ifdef MADWIFI_NG + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } +#endif /* MADWIFI_NG */ + ielen = iebuf[1]; + if (ielen == 0) { +#ifdef CONFIG_WPS + if (hapd->conf->wps_state) { + wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE " + "in (Re)Association Request - possible WPS " + "use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + return 0; + } +#endif /* CONFIG_WPS */ + printf("No WPA/RSN information element for station!?\n"); + return -1; /* XXX not right */ + } + ielen += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + iebuf, ielen, NULL, 0); + if (res != WPA_IE_OK) { + printf("WPA/RSN information element rejected? (res %u)\n", res); + return -1; + } + return 0; +} + +static int +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + int new_assoc; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_get_sta(hapd, addr); + if (sta) { + accounting_sta_stop(hapd, sta); + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + } + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } + + if (hapd->conf->wpa) { + if (madwifi_process_wpa_ie(drv, sta)) + return -1; + } + + /* + * Now that the internal station state is setup + * kick the authenticator into action. + */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + return 0; +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + madwifi_del_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(struct madwifi_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (ifi->ifi_index != drv->ifindex) + return; + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void +madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct madwifi_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + madwifi_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(void *priv) +{ + struct madwifi_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + madwifi_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); + drv->wext_sock = s; + + return 0; +} + + +static void +madwifi_wireless_event_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + if (drv != NULL) { + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); + } +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = htons(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + sta = ap_get_sta(hapd, src_addr); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA %s\n", + ether_sprintf(src_addr)); + /* XXX cannot happen */ + return; + } + ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) + goto bad; + if (hapd->conf->bridge[0] != '\0') { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + hapd->conf->bridge); + drv->sock_recv = l2_packet_init(hapd->conf->bridge, NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + madwifi_set_iface_flags(drv, 0); /* mark down during setup */ + madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + (void) madwifi_set_iface_flags(drv, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(const char *ifname, void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + return madwifi_set_iface_flags(priv, 1); +} + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .init = madwifi_init, + .deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .set_encryption = madwifi_set_key, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .wireless_event_init = madwifi_wireless_event_init, + .wireless_event_deinit = madwifi_wireless_event_deinit, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .set_ssid = madwifi_set_ssid, + .get_ssid = madwifi_get_ssid, + .set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, + .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_nl80211.c b/hostapd/driver_nl80211.c new file mode 100644 index 0000000000000..fd01d3d77d219 --- /dev/null +++ b/hostapd/driver_nl80211.c @@ -0,0 +1,2696 @@ +/* + * hostapd / Kernel driver communication via nl80211 + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include <sys/ioctl.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/msg.h> +#include <netlink/attr.h> +#include "nl80211_copy.h" +#include <net/if.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#include <linux/filter.h> +#include <net/if_arp.h> + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "hw_features.h" +#include "mlme.h" +#include "radiotap.h" +#include "radiotap_iter.h" + +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle_alloc_cb nl_socket_alloc_cb +#define nl_handle_destroy nl_socket_free +#endif /* CONFIG_LIBNL20 */ + +enum ieee80211_msg_type { + ieee80211_msg_normal = 0, + ieee80211_msg_tx_callback_ack = 1, + ieee80211_msg_tx_callback_fail = 2, +}; + +struct i802_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int bridge; + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + int eapol_sock; /* socket for EAPOL frames */ + int monitor_sock; /* socket for monitor */ + int monitor_ifidx; + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + int we_version; + struct nl_handle *nl_handle; + struct nl_cache *nl_cache; + struct nl_cb *nl_cb; + struct genl_family *nl80211; + int dtim_period, beacon_int; + unsigned int beacon_set:1; + unsigned int ieee802_1x_active:1; + + int last_freq; + int last_freq_ht; +}; + + +static void add_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + int *old; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + return; + } + } + + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = realloc(old, + sizeof(int) * (drv->num_if_indices + 1)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; +} + + +static void del_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } + } +} + + +static int have_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + + if (ifidx == drv->bridge) + return 1; + + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; + + return 0; +} + + +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct i802_driver_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + +static int hostapd_set_iface_flags(struct i802_driver_data *drv, + const char *ifname, int dev_up) +{ + struct ifreq ifr; + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)", + drv->iface); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + return 0; +} + + +static int nl_set_encr(int ifindex, struct i802_driver_data *drv, + const char *alg, const u8 *addr, int idx, const u8 *key, + size_t key_len, int txkey) +{ + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (strcmp(alg, "none") == 0) { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_KEY, 0); + } else { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_KEY, 0); + NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + if (strcmp(alg, "WEP") == 0) { + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC01); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC05); + } else if (strcmp(alg, "TKIP") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + else if (strcmp(alg, "CCMP") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + else if (strcmp(alg, "IGTK") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + else { + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm '%s'", __func__, alg); + nlmsg_free(msg); + return -1; + } + } + + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + + /* + * If we failed or don't need to set the default TX key (below), + * we're done here. + */ + if (ret || !txkey || addr) + return ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_KEY, 0); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (strcmp(alg, "IGTK") == 0) + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); + else + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_encryption(const char *iface, void *priv, const char *alg, + const u8 *addr, int idx, const u8 *key, + size_t key_len, int txkey) +{ + struct i802_driver_data *drv = priv; + int ret; + + ret = nl_set_encr(if_nametoindex(iface), drv, alg, addr, idx, key, + key_len, txkey); + if (ret < 0) + return ret; + + return ret; +} + + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + return b; +} + + +static int get_key_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the key index and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending key notifications. + */ + + if (tb[NL80211_ATTR_KEY_SEQ]) + memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), + min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + return NL_SKIP; +} + + +static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_KEY, 0); + + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + + memset(seq, 0, 6); + + return send_and_recv_msgs(drv, msg, get_key_handler, seq); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, + int mode) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; + + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_send_frame(void *priv, const void *data, size_t len, + int encrypt, int flags) +{ + __u8 rtap_hdr[] = { + 0x00, 0x00, /* radiotap version */ + 0x0e, 0x00, /* radiotap length */ + 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ + IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ + 0x00, /* padding */ + 0x00, 0x00, /* RX and TX flags to indicate that */ + 0x00, 0x00, /* this is the injected frame directly */ + }; + struct i802_driver_data *drv = priv; + struct iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void*)data, + .iov_len = len, + } + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = 2, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + return sendmsg(drv->monitor_sock, &msg, flags); +} + +static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, + int flags) +{ + struct ieee80211_mgmt *mgmt; + int do_not_encrypt = 0; + u16 fc; + + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { + /* + * Only one of the authentication frame types is encrypted. + * In order for static WEP encryption to work properly (i.e., + * to not encrypt the frame), we need to tell mac80211 about + * the frames that must not be encrypted. + */ + u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_alg == WLAN_AUTH_OPEN || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3)) + do_not_encrypt = 1; + } + + return i802_send_frame(priv, data, len, !do_not_encrypt, flags); +} + +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq2(void *priv, struct hostapd_freq_params *freq) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + drv->last_freq = freq->freq; + drv->last_freq_ht = freq->ht_enabled; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } + } + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; +} + + +static int i802_set_rts(void *priv, int rts) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + iwr.u.rts.value = rts; + iwr.u.rts.fixed = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { + perror("ioctl[SIOCSIWRTS]"); + return -1; + } + + return 0; +} + + +static int i802_get_rts(void *priv, int *rts) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { + perror("ioctl[SIOCGIWRTS]"); + return -1; + } + + *rts = iwr.u.rts.value; + + return 0; +} + + +static int i802_set_frag(void *priv, int frag) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + iwr.u.frag.value = frag; + iwr.u.frag.fixed = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { + perror("ioctl[SIOCSIWFRAG]"); + return -1; + } + + return 0; +} + + +static int i802_get_frag(void *priv, int *frag) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWFRAG, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG]"); + return -1; + } + + *frag = iwr.u.frag.value; + + return 0; +} + + +static int i802_set_retry(void *priv, int short_retry, int long_retry) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + iwr.u.retry.value = short_retry; + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCSIWRETRY(short)]"); + return -1; + } + + iwr.u.retry.value = long_retry; + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCSIWRETRY(long)]"); + return -1; + } + + return 0; +} + + +static int i802_get_retry(void *priv, int *short_retry, int *long_retry) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; + if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG(short)]"); + return -1; + } + *short_retry = iwr.u.retry.value; + + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG(long)]"); + return -1; + } + *long_retry = iwr.u.retry.value; + + return 0; +} + + +static int i802_flush(void *priv) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + /* + * XXX: FIX! this needs to flush all VLANs too + */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int get_sta_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct hostap_sta_driver_data *data = arg; + struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + }; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the interface and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending station notifications. + */ + + if (!tb[NL80211_ATTR_STA_INFO]) { + wpa_printf(MSG_DEBUG, "sta stats missing!"); + return NL_SKIP; + } + if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], + stats_policy)) { + wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); + return NL_SKIP; + } + + if (stats[NL80211_STA_INFO_INACTIVE_TIME]) + data->inactive_msec = + nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + if (stats[NL80211_STA_INFO_RX_BYTES]) + data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); + if (stats[NL80211_STA_INFO_TX_BYTES]) + data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + + return NL_SKIP; +} + +static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, get_sta_handler, data); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; +#if 0 /* FIX */ + int qos = sta->flags & WLAN_STA_WME; +#else + int qos = 0; +#endif + + len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for i802_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + hdr->frame_control |= + host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); + } +#endif + + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + pos = (u8 *) (hdr + 1); + +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + /* add an empty QoS header if needed */ + pos[0] = 0; + pos[1] = 0; + pos += 2; + } +#endif + + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + WPA_PUT_BE16(pos, ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = i802_send_frame(drv, (u8 *) hdr, len, encrypt, 0); + free(hdr); + + if (res < 0) { + perror("i802_send_eapol: send"); + printf("i802_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static int i802_sta_add2(const char *ifname, void *priv, + struct hostapd_sta_add_params *params) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, + params->supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + +#ifdef CONFIG_IEEE80211N + if (params->ht_capabilities) { + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, + params->ht_capabilities->length, + ¶ms->ht_capabilities->data); + } +#endif /* CONFIG_IEEE80211N */ + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -EEXIST) + ret = 0; + nla_put_failure: + return ret; +} + + +static int i802_sta_remove(void *priv, const u8 *addr) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + return 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg, *flags = NULL; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + flags = nlmsg_alloc(); + if (!flags) { + nlmsg_free(msg); + return -ENOMEM; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + if (total_flags & WLAN_STA_AUTHORIZED || !drv->ieee802_1x_active) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); + + if (total_flags & WLAN_STA_WME) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); + + if (total_flags & WLAN_STA_SHORT_PREAMBLE) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + + if (total_flags & WLAN_STA_MFP) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) + goto nla_put_failure; + + nlmsg_free(flags); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(flags); + return -ENOBUFS; +} + + +static int i802_set_regulatory_domain(void *priv, unsigned int rd) +{ + return -1; +} + + +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + struct nlattr *txq, *params; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); + if (!txq) + goto nla_put_failure; + + /* We are only sending parameters for a single TXQ at a time */ + params = nla_nest_start(msg, 1); + if (!params) + goto nla_put_failure; + + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + /* Burst time is configured in units of 0.1 msec and TXOP parameter in + * 32 usec, so need to convert the value here. */ + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); + + nla_nest_end(msg, params); + + nla_nest_end(msg, txq); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; +} + + +static void nl80211_remove_iface(struct i802_driver_data *drv, int ifidx) +{ + struct nl_msg *msg; + + /* stop listening for EAPOL on this interface */ + del_ifidx(drv, ifidx); + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + nla_put_failure: + printf("Failed to remove interface.\n"); +} + + +static int nl80211_create_iface(struct i802_driver_data *drv, + const char *ifname, + enum nl80211_iftype iftype, + const u8 *addr) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx; + struct ifreq ifreq; + struct iwreq iwr; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->hapd->conf->iface)); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + int err; + + flags = nlmsg_alloc(); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + + err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + + nlmsg_free(flags); + + if (err) + goto nla_put_failure; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + printf("Failed to create interface %s.\n", ifname); + return ret; + } + + ifidx = if_nametoindex(ifname); + + if (ifidx <= 0) + return -1; + + /* start listening for EAPOL on this interface */ + add_ifidx(drv, ifidx); + + if (addr) { + switch (iftype) { + case NL80211_IFTYPE_AP: + os_strlcpy(ifreq.ifr_name, ifname, IFNAMSIZ); + memcpy(ifreq.ifr_hwaddr.sa_data, addr, ETH_ALEN); + ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifreq)) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + break; + case NL80211_IFTYPE_WDS: + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); + iwr.u.addr.sa_family = ARPHRD_ETHER; + memcpy(iwr.u.addr.sa_data, addr, ETH_ALEN); + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr)) + return -1; + break; + default: + /* nothing */ + break; + } + } + + return ifidx; +} + + +static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid) +{ + int ifidx; + + /* + * The kernel supports that when the low-level driver does, + * but we currently don't because we need per-BSS data that + * currently we can't handle easily. + */ + return -1; + + ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid); + if (ifidx < 0) + return -1; + if (hostapd_set_iface_flags(priv, ifname, 1)) { + nl80211_remove_iface(priv, ifidx); + return -1; + } + return 0; +} + + +static int i802_bss_remove(void *priv, const char *ifname) +{ + nl80211_remove_iface(priv, if_nametoindex(ifname)); + return 0; +} + + +static int i802_set_beacon(const char *iface, void *priv, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + u8 cmd = NL80211_CMD_NEW_BEACON; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (drv->beacon_set) + cmd = NL80211_CMD_SET_BEACON; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, cmd, 0); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int); + + if (!drv->dtim_period) + drv->dtim_period = 2; + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + drv->beacon_set = 1; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_del_beacon(struct i802_driver_data *drv) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct i802_driver_data *drv = priv; + + /* + * FIXME: This needs to be per interface (BSS) + */ + drv->ieee802_1x_active = enabled; + return 0; +} + + +static int i802_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + + os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); + iwr.u.param.flags = IW_AUTH_PRIVACY_INVOKED; + iwr.u.param.value = enabled; + + ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr); + + /* ignore errors, the kernel/driver might not care */ + return 0; +} + + +static int i802_set_internal_bridge(void *priv, int value) +{ + return -1; +} + + +static int i802_set_beacon_int(void *priv, int value) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + drv->beacon_int = value; + + if (!drv->beacon_set) + return 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_dtim_period(const char *iface, void *priv, int value) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + + drv->dtim_period = value; + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_bss(void *priv, int cts, int preamble, int slot) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_cts_protect(void *priv, int value) +{ + return i802_set_bss(priv, value, -1, -1); +} + + +static int i802_set_preamble(void *priv, int value) +{ + return i802_set_bss(priv, -1, value, -1); +} + + +static int i802_set_short_slot_time(void *priv, int value) +{ + return i802_set_bss(priv, -1, -1, value); +} + + +static enum nl80211_iftype i802_if_type(enum hostapd_driver_if_type type) +{ + switch (type) { + case HOSTAPD_IF_VLAN: + return NL80211_IFTYPE_AP_VLAN; + case HOSTAPD_IF_WDS: + return NL80211_IFTYPE_WDS; + } + return -1; +} + + +static int i802_if_add(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr) +{ + if (nl80211_create_iface(priv, ifname, i802_if_type(type), addr) < 0) + return -1; + return 0; +} + + +static int i802_if_update(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + /* unused at the moment */ + return -1; +} + + +static int i802_if_remove(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr) +{ + nl80211_remove_iface(priv, if_nametoindex(ifname)); + return 0; +} + + +struct phy_info_arg { + u16 *num_modes; + struct hostapd_hw_modes *modes; +}; + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + }; + + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, + }; + + struct nlattr *nl_band; + struct nlattr *nl_freq; + struct nlattr *nl_rate; + int rem_band, rem_freq, rem_rate; + struct hostapd_hw_modes *mode; + int idx, mode_is_set; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { + mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode_is_set = 0; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + memset(mode, 0, sizeof(*mode)); + *(phy_info->num_modes) += 1; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { + mode->ht_capab = nla_get_u16( + tb_band[NL80211_BAND_ATTR_HT_CAPA]); + } + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + mode->num_channels++; + } + + mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data)); + if (!mode->channels) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + mode->channels[idx].flag = 0; + + if (!mode_is_set) { + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + mode->mode = HOSTAPD_MODE_IEEE80211B; + else + mode->mode = HOSTAPD_MODE_IEEE80211A; + mode_is_set = 1; + } + + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + if (mode->channels[idx].freq == 2848) + mode->channels[idx].chan = 14; + else + mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; + else + mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_PASSIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_NO_IBSS; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_RADAR; + + if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && + !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].max_tx_power = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; + + idx++; + } + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); + + /* crude heuristic */ + if (mode->mode == HOSTAPD_MODE_IEEE80211B && + mode->rates[idx].rate > 200) + mode->mode = HOSTAPD_MODE_IEEE80211G; + + if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) + mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2; + + idx++; + } + } + + return NL_SKIP; +} + +static struct hostapd_hw_modes *i802_add_11b(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(struct hostapd_rate_data)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i].rate > 110 || + mode11g->rates[i].flags & + (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM)) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); + + return modes; +} + +static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + }; + + *num_modes = 0; + *flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return i802_add_11b(result.modes, num_modes); + nla_put_failure: + return NULL; +} + + +static int i802_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_country(void *priv, const char *country) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + char alpha2[3]; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + alpha2[0] = country[0]; + alpha2[1] = country[1]; + alpha2[2] = '\0'; + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static void handle_unknown_sta(struct hostapd_data *hapd, u8 *ta) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, ta); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data/PS-poll frame from not associated STA " + MACSTR "\n", MAC2STR(ta)); + if (sta && (sta->flags & WLAN_STA_AUTH)) + hostapd_sta_disassoc( + hapd, ta, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hostapd_sta_deauth( + hapd, ta, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + } +} + + +static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, + int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_MGMT: + wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", + ok ? "ACK" : "fail"); + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", + ok ? "ACK" : "fail"); + break; + case WLAN_FC_TYPE_DATA: + sta = ap_get_sta(hapd, hdr->addr1); + if (sta && sta->flags & WLAN_STA_PENDING_POLL) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " + "activity poll", MAC2STR(sta->addr), + ok ? "ACKed" : "did not ACK"); + if (ok) + sta->flags &= ~WLAN_STA_PENDING_POLL; + } + if (sta) + ieee802_1x_tx_status(hapd, sta, buf, len, ok); + break; + default: + printf("unknown TX callback frame type %d\n", type); + break; + } +} + + +static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, + struct hostapd_frame_info *hfi, + enum ieee80211_msg_type msg_type) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + size_t data_len = len; + struct hostapd_data *hapd = NULL; + int broadcast_bssid = 0; + size_t i; + u8 *bssid; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_TODS: + bssid = hdr->addr1; + break; + case WLAN_FC_FROMDS: + bssid = hdr->addr2; + break; + default: + /* discard */ + return; + } + break; + case WLAN_FC_TYPE_CTRL: + /* discard non-ps-poll frames */ + if (stype != WLAN_FC_STYPE_PSPOLL) + return; + bssid = hdr->addr1; + break; + case WLAN_FC_TYPE_MGMT: + bssid = hdr->addr3; + break; + default: + /* discard */ + return; + } + + /* find interface frame belongs to */ + for (i = 0; i < iface->num_bss; i++) { + if (memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) { + hapd = iface->bss[i]; + break; + } + } + + if (hapd == NULL) { + hapd = iface->bss[0]; + + if (bssid[0] != 0xff || bssid[1] != 0xff || + bssid[2] != 0xff || bssid[3] != 0xff || + bssid[4] != 0xff || bssid[5] != 0xff) { + /* + * Unknown BSSID - drop frame if this is not from + * passive scanning or a beacon (at least ProbeReq + * frames to other APs may be allowed through RX + * filtering in the wlan hw/driver) + */ + if ((type != WLAN_FC_TYPE_MGMT || + stype != WLAN_FC_STYPE_BEACON)) + return; + } else + broadcast_bssid = 1; + } + + switch (msg_type) { + case ieee80211_msg_normal: + /* continue processing */ + break; + case ieee80211_msg_tx_callback_ack: + handle_tx_callback(hapd, buf, data_len, 1); + return; + case ieee80211_msg_tx_callback_fail: + handle_tx_callback(hapd, buf, data_len, 0); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + if (stype != WLAN_FC_STYPE_BEACON && + stype != WLAN_FC_STYPE_PROBE_REQ) + wpa_printf(MSG_MSGDUMP, "MGMT"); + if (broadcast_bssid) { + for (i = 0; i < iface->num_bss; i++) + ieee802_11_mgmt(iface->bss[i], buf, data_len, + stype, hfi); + } else + ieee802_11_mgmt(hapd, buf, data_len, stype, hfi); + break; + case WLAN_FC_TYPE_CTRL: + /* can only get here with PS-Poll frames */ + wpa_printf(MSG_DEBUG, "CTRL"); + handle_unknown_sta(hapd, hdr->addr2); + break; + case WLAN_FC_TYPE_DATA: + handle_unknown_sta(hapd, hdr->addr2); + break; + } +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct i802_driver_data *drv = eloop_ctx; + struct hostapd_data *hapd = drv->hapd; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); + + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + perror("recv"); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + ieee802_1x_receive(hapd, lladdr.sll_addr, buf, len); +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct i802_driver_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct hostapd_data *hapd = drv->hapd; + struct ieee80211_radiotap_iterator iter; + int ret; + struct hostapd_frame_info hfi; + int injected = 0, failed = 0, msg_type, rxflags = 0; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { + printf("received invalid radiotap frame\n"); + return; + } + + memset(&hfi, 0, sizeof(hfi)); + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + printf("received invalid radiotap frame (%d)\n", ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO convert from freq/flags to channel number + hfi.channel = XXX; + hfi.phytype = XXX; + */ + break; + case IEEE80211_RADIOTAP_RATE: + hfi.datarate = *iter.this_arg * 5; + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + hfi.ssi_signal = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) + msg_type = ieee80211_msg_normal; + else if (failed) + msg_type = ieee80211_msg_tx_callback_fail; + else + msg_type = ieee80211_msg_tx_callback_ack; + + handle_frame(hapd->iface, buf + iter.max_length, + len - iter.max_length, &hfi, msg_type); +} + + +/* + * we post-process the filter code later and rewrite + * this to the offset to the last instruction + */ +#define PASS 0xFF +#define FAIL 0xFE + +static struct sock_filter msock_filter_insns[] = { + /* + * do a little-endian load of the radiotap length field + */ + /* load lower byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + /* put it into X (== index register) */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + /* load upper byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), + /* left-shift it by 8 */ + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), + /* or with X */ + BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), + /* put result into X */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + + /* + * Allow management frames through, this also gives us those + * management frames that we sent ourselves with status + */ + /* load the lower byte of the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off frame type and version */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), + /* accept frame if it's both 0, fall through otherwise */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + + /* + * TODO: add a bit to radiotap RX flags that indicates + * that the sending station is not associated, then + * add a filter here that filters on our DA and that flag + * to allow us to deauth frames to that bad station. + * + * Not a regression -- we didn't do it before either. + */ + +#if 0 + /* + * drop non-data frames, WDS frames + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), + /* drop non-data frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* drop WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), +#endif + + /* + * add header length to index + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), + /* right shift it by 6 to give 0 or 2 */ + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), + /* add data frame header length */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), + /* add index, was start of 802.11 header */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + /* move to index, now start of LL header */ + BPF_STMT(BPF_MISC | BPF_TAX, 0), + + /* + * Accept empty data frames, we use those for + * polling activity. + */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + + /* + * Accept EAPOL frames + */ + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), + + /* keep these last two statements or change the code below */ + /* return 0 == "DROP" */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return ~0 == "keep all" */ + BPF_STMT(BPF_RET | BPF_K, ~0), +}; + +static struct sock_fprog msock_filter = { + .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), + .filter = msock_filter_insns, +}; + + +static int add_monitor_filter(int s) +{ + int idx; + + /* rewrite all PASS/FAIL jump offsets */ + for (idx = 0; idx < msock_filter.len; idx++) { + struct sock_filter *insn = &msock_filter_insns[idx]; + + if (BPF_CLASS(insn->code) == BPF_JMP) { + if (insn->code == (BPF_JMP|BPF_JA)) { + if (insn->k == PASS) + insn->k = msock_filter.len - idx - 2; + else if (insn->k == FAIL) + insn->k = msock_filter.len - idx - 3; + } + + if (insn->jt == PASS) + insn->jt = msock_filter.len - idx - 2; + else if (insn->jt == FAIL) + insn->jt = msock_filter.len - idx - 3; + + if (insn->jf == PASS) + insn->jf = msock_filter.len - idx - 2; + else if (insn->jf == FAIL) + insn->jf = msock_filter.len - idx - 3; + } + } + + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &msock_filter, sizeof(msock_filter))) { + perror("SO_ATTACH_FILTER"); + return -1; + } + + return 0; +} + + +static int nl80211_create_monitor_interface(struct i802_driver_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval; + socklen_t optlen; + + snprintf(buf, IFNAMSIZ, "mon.%s", drv->iface); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL); + + if (drv->monitor_ifidx < 0) + return -1; + + if (hostapd_set_iface_flags(drv, buf, 1)) + goto error; + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + goto error; + } + + if (add_monitor_filter(drv->monitor_sock)) { + wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " + "interface; do filtering in user space"); + /* This works, but will cost in performance. */ + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, + sizeof(ll)) < 0) { + perror("monitor socket bind"); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + perror("Failed to set socket priority"); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + printf("Could not register monitor read socket\n"); + goto error; + } + + return 0; + error: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + + +static int nl80211_set_master_mode(struct i802_driver_data *drv, + const char *ifname) +{ + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + nla_put_failure: + wpa_printf(MSG_ERROR, "Failed to set interface %s to master " + "mode.", ifname); + return ret; +} + + +static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + return -1; + } + + /* start listening for EAPOL on the default AP interface */ + add_ifidx(drv, if_nametoindex(drv->iface)); + + if (hostapd_set_iface_flags(drv, drv->iface, 0)) + return -1; + + if (bssid) { + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) { + perror("ioctl(SIOCSIFHWADDR)"); + return -1; + } + } + + /* + * initialise generic netlink and nl80211 + */ + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + printf("Failed to allocate netlink callbacks.\n"); + return -1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + if (!drv->nl_handle) { + printf("Failed to allocate netlink handle.\n"); + return -1; + } + + if (genl_connect(drv->nl_handle)) { + printf("Failed to connect to generic netlink.\n"); + return -1; + } + +#ifdef CONFIG_LIBNL20 + if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { + printf("Failed to allocate generic netlink cache.\n"); + return -1; + } +#else /* CONFIG_LIBNL20 */ + drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); + if (!drv->nl_cache) { + printf("Failed to allocate generic netlink cache.\n"); + return -1; + } +#endif /* CONFIG_LIBNL20 */ + + drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); + if (!drv->nl80211) { + printf("nl80211 not found.\n"); + return -1; + } + + /* Initialise a monitor interface */ + if (nl80211_create_monitor_interface(drv)) + return -1; + + if (nl80211_set_master_mode(drv, drv->iface)) + goto fail1; + + if (hostapd_set_iface_flags(drv, drv->iface, 1)) + goto fail1; + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); + if (drv->eapol_sock < 0) { + perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); + goto fail1; + } + + if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) + { + printf("Could not register read socket for eapol\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + goto fail1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + goto fail1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; + +fail1: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + + +static int i802_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_sta_driver_data data; + int ret; + + data.inactive_msec = (unsigned long) -1; + ret = i802_read_sta_data(priv, &data, addr); + if (ret || data.inactive_msec == (unsigned long) -1) + return -1; + return data.inactive_msec / 1000; +} + + +static int i802_sta_clear_stats(void *priv, const u8 *addr) +{ +#if 0 + /* TODO */ +#endif + return 0; +} + + +static void +hostapd_wireless_event_wireless_custom(struct i802_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct i802_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(struct i802_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void hostapd_wireless_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct i802_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + hostapd_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int hostap_get_we_version(struct i802_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int i802_wireless_event_init(void *priv) +{ + struct i802_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + hostap_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, + NULL); + drv->wext_sock = s; + + return 0; +} + + +static void i802_wireless_event_deinit(void *priv) +{ + struct i802_driver_data *drv = priv; + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); +} + + +static int i802_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0); +} + + +static int i802_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0); +} + + +static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) +{ + struct i802_driver_data *drv; + + drv = os_zalloc(sizeof(struct i802_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for i802 driver data\n"); + return NULL; + } + + drv->hapd = hapd; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + drv->bridge = if_nametoindex(hapd->conf->bridge); + + if (i802_init_sockets(drv, bssid)) + goto failed; + + return drv; + +failed: + free(drv); + return NULL; +} + + +static void *i802_init(struct hostapd_data *hapd) +{ + return i802_init_bssid(hapd, NULL); +} + + +static void i802_deinit(void *priv) +{ + struct i802_driver_data *drv = priv; + + if (drv->last_freq_ht) { + /* Clear HT flags from the driver */ + struct hostapd_freq_params freq; + os_memset(&freq, 0, sizeof(freq)); + freq.freq = drv->last_freq; + i802_set_freq2(priv, &freq); + } + + i802_del_beacon(drv); + + /* remove monitor interface */ + nl80211_remove_iface(drv, drv->monitor_ifidx); + + (void) hostapd_set_iface_flags(drv, drv->iface, 0); + + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + } + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->eapol_sock >= 0) { + eloop_unregister_read_sock(drv->eapol_sock); + close(drv->eapol_sock); + } + + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + if (drv->if_indices != drv->default_if_indices) + free(drv->if_indices); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .init = i802_init, + .init_bssid = i802_init_bssid, + .deinit = i802_deinit, + .wireless_event_init = i802_wireless_event_init, + .wireless_event_deinit = i802_wireless_event_deinit, + .set_ieee8021x = i802_set_ieee8021x, + .set_privacy = i802_set_privacy, + .set_encryption = i802_set_encryption, + .get_seqnum = i802_get_seqnum, + .flush = i802_flush, + .read_sta_data = i802_read_sta_data, + .send_eapol = i802_send_eapol, + .sta_set_flags = i802_sta_set_flags, + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, + .sta_remove = i802_sta_remove, + .send_mgmt_frame = i802_send_mgmt_frame, + .sta_add2 = i802_sta_add2, + .get_inact_sec = i802_get_inact_sec, + .sta_clear_stats = i802_sta_clear_stats, + .set_freq2 = i802_set_freq2, + .set_rts = i802_set_rts, + .get_rts = i802_get_rts, + .set_frag = i802_set_frag, + .get_frag = i802_get_frag, + .set_retry = i802_set_retry, + .get_retry = i802_get_retry, + .set_rate_sets = i802_set_rate_sets, + .set_regulatory_domain = i802_set_regulatory_domain, + .set_beacon = i802_set_beacon, + .set_internal_bridge = i802_set_internal_bridge, + .set_beacon_int = i802_set_beacon_int, + .set_dtim_period = i802_set_dtim_period, + .set_cts_protect = i802_set_cts_protect, + .set_preamble = i802_set_preamble, + .set_short_slot_time = i802_set_short_slot_time, + .set_tx_queue_params = i802_set_tx_queue_params, + .bss_add = i802_bss_add, + .bss_remove = i802_bss_remove, + .if_add = i802_if_add, + .if_update = i802_if_update, + .if_remove = i802_if_remove, + .get_hw_feature_data = i802_get_hw_feature_data, + .set_sta_vlan = i802_set_sta_vlan, + .set_country = i802_set_country, +}; diff --git a/hostapd/driver_none.c b/hostapd/driver_none.c new file mode 100644 index 0000000000000..96e7e644e5265 --- /dev/null +++ b/hostapd/driver_none.c @@ -0,0 +1,62 @@ +/* + * hostapd / Driver interface for RADIUS server only (no driver) + * Copyright (c) 2008, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "driver.h" + + +struct none_driver_data { + struct hostapd_data *hapd; +}; + + +static void * none_driver_init(struct hostapd_data *hapd) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->hapd = hapd; + + return drv; +} + + +static void none_driver_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + return 0; +} + + +const struct wpa_driver_ops wpa_driver_none_ops = { + .name = "none", + .init = none_driver_init, + .deinit = none_driver_deinit, + .send_ether = none_driver_send_ether, +}; diff --git a/hostapd/driver_prism54.c b/hostapd/driver_prism54.c new file mode 100644 index 0000000000000..76bfcd2d0d919 --- /dev/null +++ b/hostapd/driver_prism54.c @@ -0,0 +1,1091 @@ +/* + * hostapd / Driver interaction with Prism54 PIMFOR interface + * Copyright (c) 2004, Bell Kin <bell_kin@pek.com.tw> + * based on hostap driver.c, ieee802_11.c + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <sys/select.h> + +#ifdef USE_KERNEL_HEADERS +/* compat-wireless does not include linux/compiler.h to define __user, so + * define it here */ +#ifndef __user +#define __user +#endif /* __user */ +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/wireless.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "prism54.h" +#include "wpa.h" +#include "radius/radius.h" +#include "sta_info.h" +#include "accounting.h" + +const int PIM_BUF_SIZE = 4096; + +struct prism54_driver_data { + struct hostapd_data *hapd; + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for 802.3 access */ + int pim_sock; /* socket for pimfor packet */ + char macs[2007][6]; +}; + + +static int mac_id_refresh(struct prism54_driver_data *data, int id, char *mac) +{ + if (id < 0 || id > 2006) { + return -1; + } + memcpy(&data->macs[id][0], mac, ETH_ALEN); + return 0; +} + + +static char * mac_id_get(struct prism54_driver_data *data, int id) +{ + if (id < 0 || id > 2006) { + return NULL; + } + return &data->macs[id][0]; +} + + +/* wait for a specific pimfor, timeout in 10ms resolution */ +/* pim_sock must be non-block to prevent dead lock from no response */ +/* or same response type in series */ +static int prism54_waitpim(void *priv, unsigned long oid, void *buf, int len, + int timeout) +{ + struct prism54_driver_data *drv = priv; + struct timeval tv, stv, ctv; + fd_set pfd; + int rlen; + pimdev_hdr *pkt; + + pkt = malloc(8192); + if (pkt == NULL) + return -1; + + FD_ZERO(&pfd); + gettimeofday(&stv, NULL); + do { + FD_SET(drv->pim_sock, &pfd); + tv.tv_sec = 0; + tv.tv_usec = 10000; + if (select(drv->pim_sock + 1, &pfd, NULL, NULL, &tv)) { + rlen = recv(drv->pim_sock, pkt, 8192, 0); + if (rlen > 0) { + if (pkt->oid == htonl(oid)) { + if (rlen <= len) { + if (buf != NULL) { + memcpy(buf, pkt, rlen); + } + free(pkt); + return rlen; + } else { + printf("buffer too small\n"); + free(pkt); + return -1; + } + } else { + gettimeofday(&ctv, NULL); + continue; + } + } + } + gettimeofday(&ctv, NULL); + } while (((ctv.tv_sec - stv.tv_sec) * 100 + + (ctv.tv_usec - stv.tv_usec) / 10000) > timeout); + free(pkt); + return 0; +} + + +/* send an eapol packet */ +static int prism54_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct prism54_driver_data *drv = priv; + ieee802_3_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for prism54_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + memcpy(&hdr->da[0], addr, ETH_ALEN); + memcpy(&hdr->sa[0], own_addr, ETH_ALEN); + hdr->type = htons(ETH_P_PAE); + pos = (u8 *) (hdr + 1); + memcpy(pos, data, data_len); + + res = send(drv->sock, hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("hostapd_send_eapol: send"); + printf("hostapd_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +/* open data channel(auth-1) or eapol only(unauth-0) */ +static int prism54_set_sta_authorized(void *priv, const u8 *addr, + int authorized) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + char *pos; + + hdr = malloc(sizeof(*hdr) + ETH_ALEN); + if (hdr == NULL) + return -1; + hdr->op = htonl(PIMOP_SET); + if (authorized) { + hdr->oid = htonl(DOT11_OID_EAPAUTHSTA); + } else { + hdr->oid = htonl(DOT11_OID_EAPUNAUTHSTA); + } + pos = (char *) (hdr + 1); + memcpy(pos, addr, ETH_ALEN); + send(drv->pim_sock, hdr, sizeof(*hdr) + ETH_ALEN, 0); + prism54_waitpim(priv, hdr->oid, hdr, sizeof(*hdr) + ETH_ALEN, 10); + free(hdr); + return 0; +} + + +static int +prism54_sta_set_flags(void *priv, const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return prism54_set_sta_authorized(priv, addr, 1); + if (flags_and & WLAN_STA_AUTHORIZED) + return prism54_set_sta_authorized(priv, addr, 0); + return 0; +} + + +/* set per station key */ +static int prism54_set_encryption(const char *ifname, void *priv, + const char *alg, const u8 *addr, + int idx, const u8 *key, size_t key_len, + int txkey) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_stakey *keys; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(struct obj_stakey) + sizeof(pimdev_hdr); + hdr = malloc(blen); + if (hdr == NULL) { + printf("memory low\n"); + return -1; + } + keys = (struct obj_stakey *) &hdr[1]; + if (!addr) { + memset(&keys->address[0], 0xff, ETH_ALEN); + } else { + memcpy(&keys->address[0], addr, ETH_ALEN); + } + if (!strcmp(alg, "WEP")) { + keys->type = DOT11_PRIV_WEP; + } else if (!strcmp(alg, "TKIP")) { + keys->type = DOT11_PRIV_TKIP; + } else if (!strcmp(alg, "none")) { + /* the only way to clear the key is to deauth it */ + /* and prism54 is capable to receive unencrypted packet */ + /* so we do nothing here */ + free(hdr); + return 0; + } else { + printf("bad auth type: %s\n", alg); + } + buf = (u8 *) &keys->key[0]; + keys->length = key_len; + keys->keyid = idx; + keys->options = htons(DOT11_STAKEY_OPTION_DEFAULTKEY); + keys->reserved = 0; + + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_STAKEY); + + memcpy(buf, key, key_len); + + ret = send(drv->pim_sock, hdr, blen, 0); + if (ret < 0) { + free(hdr); + return ret; + } + prism54_waitpim(priv, hdr->oid, hdr, blen, 10); + + free(hdr); + + return 0; +} + + +/* get TKIP station sequence counter, prism54 is only 6 bytes */ +static int prism54_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct prism54_driver_data *drv = priv; + struct obj_stasc *stasc; + pimdev_hdr *hdr; + size_t blen; + int ret = 0; + + blen = sizeof(*stasc) + sizeof(*hdr); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + + stasc = (struct obj_stasc *) &hdr[1]; + + if (addr == NULL) + memset(&stasc->address[0], 0xff, ETH_ALEN); + else + memcpy(&stasc->address[0], addr, ETH_ALEN); + + hdr->oid = htonl(DOT11_OID_STASC); + hdr->op = htonl(PIMOP_GET); + stasc->keyid = idx; + if (send(drv->pim_sock,hdr,blen,0) <= 0) { + free(hdr); + return -1; + } + if (prism54_waitpim(priv, DOT11_OID_STASC, hdr, blen, 10) <= 0) { + ret = -1; + } else { + if (hdr->op == (int) htonl(PIMOP_RESPONSE)) { + memcpy(seq + 2, &stasc->sc_high, ETH_ALEN); + memset(seq, 0, 2); + } else { + ret = -1; + } + } + free(hdr); + + return ret; +} + + +/* include unencrypted, set mlme autolevel to extended */ +static int prism54_init_1x(void *priv) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + unsigned long *ul; + int blen = sizeof(*hdr) + sizeof(*ul); + + hdr = malloc(blen); + if (hdr == NULL) + return -1; + + ul = (unsigned long *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_EXUNENCRYPTED); + *ul = htonl(DOT11_BOOL_TRUE); /* not accept */ + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_EXUNENCRYPTED, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_MLMEAUTOLEVEL); + *ul = htonl(DOT11_MLME_EXTENDED); + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_MLMEAUTOLEVEL, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DOT1XENABLE); + *ul = htonl(DOT11_BOOL_TRUE); + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DOT1XENABLE, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENABLE); + *ul = htonl(DOT11_AUTH_OS); /* OS */ + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_AUTHENABLE, hdr, blen, 10); + free(hdr); + return 0; +} + + +static int prism54_set_privacy_invoked(const char *ifname, void *priv, + int flag) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + unsigned long *ul; + int ret; + int blen = sizeof(*hdr) + sizeof(*ul); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + ul = (unsigned long *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_PRIVACYINVOKED); + if (flag) { + *ul = htonl(DOT11_BOOL_TRUE); /* has privacy */ + } else { + *ul = 0; + } + ret = send(drv->pim_sock, hdr, blen, 0); + if (ret >= 0) { + ret = prism54_waitpim(priv, DOT11_OID_PRIVACYINVOKED, hdr, + blen, 10); + } + free(hdr); + return ret; +} + + +static int prism54_ioctl_setiwessid(const char *ifname, void *priv, + const u8 *buf, int len) +{ +#if 0 + struct prism54_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->pim_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } +#endif + return 0; +} + + +/* kick all stations */ +/* does not work during init, but at least it won't crash firmware */ +static int prism54_flush(void *priv) +{ + struct prism54_driver_data *drv = priv; + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + int ret; + unsigned int i; + long *nsta; + int blen = sizeof(*hdr) + sizeof(*mlme); + char *mac_id; + + hdr = os_zalloc(blen); + if (hdr == NULL) + return -1; + + mlme = (struct obj_mlmeex *) &hdr[1]; + nsta = (long *) &hdr[1]; + hdr->op = htonl(PIMOP_GET); + hdr->oid = htonl(DOT11_OID_CLIENTS); + ret = send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(long), 0); + ret = prism54_waitpim(priv, DOT11_OID_CLIENTS, hdr, blen, 10); + if ((ret < 0) || (hdr->op != (int) htonl(PIMOP_RESPONSE)) || + (le_to_host32(*nsta) > 2007)) { + free(hdr); + return 0; + } + for (i = 0; i < le_to_host32(*nsta); i++) { + mlme->id = -1; + mac_id = mac_id_get(drv, i); + if (mac_id) + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); + mlme->state = htons(DOT11_STATE_NONE); + mlme->size = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, + 100); + } + for (i = 0; i < le_to_host32(*nsta); i++) { + mlme->id = -1; + mac_id = mac_id_get(drv, i); + if (mac_id) + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); + mlme->state = htons(DOT11_STATE_NONE); + mlme->size = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, + 100); + } + free(hdr); + return 0; +} + + +static int prism54_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + int ret; + int blen = sizeof(*hdr) + sizeof(*mlme); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + mlme = (struct obj_mlmeex *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + memcpy(&mlme->address[0], addr, ETH_ALEN); + mlme->id = -1; + mlme->state = htons(DOT11_STATE_NONE); + mlme->code = host_to_le16(reason); + mlme->size = 0; + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, 10); + free(hdr); + return ret; +} + + +static int prism54_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + int ret; + int blen = sizeof(*hdr) + sizeof(*mlme); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + mlme = (struct obj_mlmeex *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + memcpy(&mlme->address[0], addr, ETH_ALEN); + mlme->id = -1; + mlme->state = htons(DOT11_STATE_NONE); + mlme->code = host_to_le16(reason); + mlme->size = 0; + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, 10); + free(hdr); + return ret; +} + + +static int prism54_get_inact_sec(void *priv, const u8 *addr) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_sta *sta; + int blen = sizeof(*hdr) + sizeof(*sta); + int ret; + + hdr = malloc(blen); + if (hdr == NULL) + return -1; + hdr->op = htonl(PIMOP_GET); + hdr->oid = htonl(DOT11_OID_CLIENTFIND); + sta = (struct obj_sta *) &hdr[1]; + memcpy(&sta->address[0], addr, ETH_ALEN); + ret = send(drv->pim_sock, hdr, blen, 0); + ret = prism54_waitpim(priv, DOT11_OID_CLIENTFIND, hdr, blen, 10); + if (ret != blen) { + printf("get_inact_sec: bad return %d\n", ret); + free(hdr); + return -1; + } + if (hdr->op != (int) htonl(PIMOP_RESPONSE)) { + printf("get_inact_sec: bad resp\n"); + free(hdr); + return -1; + } + free(hdr); + return le_to_host16(sta->age); +} + + +/* set attachments */ +static int prism54_set_generic_elem(const char *ifname, void *priv, + const u8 *elem, size_t elem_len) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + char *pos; + struct obj_attachment_hdr *attach; + size_t blen = sizeof(*hdr) + sizeof(*attach) + elem_len; + hdr = os_zalloc(blen); + if (hdr == NULL) { + printf("%s: memory low\n", __func__); + return -1; + } + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_ATTACHMENT); + attach = (struct obj_attachment_hdr *)&hdr[1]; + attach->type = DOT11_PKT_BEACON; + attach->id = -1; + attach->size = host_to_le16((short)elem_len); + pos = ((char*) attach) + sizeof(*attach); + if (elem) + memcpy(pos, elem, elem_len); + send(drv->pim_sock, hdr, blen, 0); + attach->type = DOT11_PKT_PROBE_RESP; + send(drv->pim_sock, hdr, blen, 0); + free(hdr); + return 0; +} + + +/* tell the card to auth the sta */ +static void prism54_handle_probe(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + hdr = (pimdev_hdr *)buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta != NULL) { + if (sta->flags & (WLAN_STA_AUTH | WLAN_STA_ASSOC)) + return; + } + if (len < sizeof(*mlme)) { + printf("bad probe packet\n"); + return; + } + mlme->state = htons(DOT11_STATE_AUTHING); + mlme->code = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); + mlme->size = 0; + send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); +} + + +static void prism54_handle_deauth(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlme *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + char *mac_id; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlme *) &hdr[1]; + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + mac_id = mac_id_get(drv, mlme->id); + if (sta == NULL || mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(drv->hapd, sta); +} + + +static void prism54_handle_disassoc(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlme *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + char *mac_id; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlme *) &hdr[1]; + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + return; + } + sta->flags &= ~WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + accounting_sta_stop(drv->hapd, sta); + ieee802_1x_free_station(sta); +} + + +/* to auth it, just allow it now, later for os/sk */ +static void prism54_handle_auth(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + int resp; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + if (len < sizeof(*mlme)) { + printf("bad auth packet\n"); + return; + } + + if (mlme->state == htons(DOT11_STATE_AUTHING)) { + sta = ap_sta_add(drv->hapd, (u8 *) &mlme->address[0]); + if (drv->hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + mac_id_refresh(drv, mlme->id, &mlme->address[0]); + if (!sta) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->flags &= ~WLAN_STA_PREAUTH; + + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + mlme->code = 0; + mlme->state=htons(DOT11_STATE_AUTH); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); + mlme->size = 0; + sta->timeout_next = STA_NULLFUNC; + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); + } + return; + +fail: + printf("auth fail: %x\n", resp); + mlme->code = host_to_le16(resp); + mlme->size = 0; + if (sta) + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + hdr->op = htonl(PIMOP_SET); + send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); +} + + +/* do the wpa thing */ +static void prism54_handle_assoc(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + struct ieee802_11_elems elems; + struct sta_info *sta; + u8 *wpa_ie; + u8 *cb; + int ieofs = 0; + size_t wpa_ie_len; + int resp, new_assoc; + char *mac_id; + + resp = 0; + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + switch (ntohl(hdr->oid)) { + case DOT11_OID_ASSOCIATE: + case DOT11_OID_REASSOCIATE: + mlme->size = 0; + default: + break; + } + if ((mlme->state == (int) htonl(DOT11_STATE_ASSOCING)) || + (mlme->state == (int) htonl(DOT11_STATE_REASSOCING))) { + if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { + printf("bad assoc packet\n"); + return; + } + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + printf("cannot get sta\n"); + return; + } + cb = (u8 *) &mlme->data[0]; + if (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) { + ieofs = 4; + } else if (hdr->oid == htonl(DOT11_OID_REASSOCIATEEX)) { + ieofs = 10; + } + if (le_to_host16(mlme->size) <= ieofs) { + printf("attach too small\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (ieee802_11_parse_elems(cb + ieofs, + le_to_host16(mlme->size) - ieofs, + &elems, 1) == ParseFailed) { + printf("STA " MACSTR " sent invalid association " + "request\n", MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if ((drv->hapd->conf->wpa & WPA_PROTO_RSN) && + elems.rsn_ie) { + wpa_ie = elems.rsn_ie; + wpa_ie_len = elems.rsn_ie_len; + } else if ((drv->hapd->conf->wpa & WPA_PROTO_WPA) && + elems.wpa_ie) { + wpa_ie = elems.wpa_ie; + wpa_ie_len = elems.wpa_ie_len; + } else { + wpa_ie = NULL; + wpa_ie_len = 0; + } + if (drv->hapd->conf->wpa && wpa_ie == NULL) { + printf("STA " MACSTR ": No WPA/RSN IE in association " + "request\n", MAC2STR(sta->addr)); + resp = WLAN_STATUS_INVALID_IE; + goto fail; + } + if (drv->hapd->conf->wpa) { + int res; + wpa_ie -= 2; + wpa_ie_len += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init( + drv->hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state " + "machine\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + res = wpa_validate_wpa_ie(drv->hapd->wpa_auth, + sta->wpa_sm, + wpa_ie, wpa_ie_len, + NULL, 0); + if (res == WPA_INVALID_GROUP) + resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_PAIRWISE) + resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_AKMP) + resp = WLAN_STATUS_AKMP_NOT_VALID; + else if (res == WPA_ALLOC_FAIL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else if (res != WPA_IE_OK) + resp = WLAN_STATUS_INVALID_IE; + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + } + hdr->oid = (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) ? + htonl(DOT11_OID_ASSOCIATEEX) : + htonl(DOT11_OID_REASSOCIATEEX); + hdr->op = htonl(PIMOP_SET); + mlme->code = 0; + mlme->state = htons(DOT11_STATE_ASSOC); + mlme->size = 0; + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); + return; + } else if (mlme->state==htons(DOT11_STATE_ASSOC)) { + if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { + printf("bad assoc packet\n"); + return; + } + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + printf("cannot get sta\n"); + return; + } + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(drv->hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + sta->timeout_next = STA_NULLFUNC; + return; + } + return; + +fail: + printf("Prism54: assoc fail: %x\n", resp); + mlme->code = host_to_le16(resp); + mlme->size = 0; + mlme->state = htons(DOT11_STATE_ASSOCING); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + hdr->op = htonl(PIMOP_SET); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); +} + + +static void handle_pim(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct prism54_driver_data *drv = eloop_ctx; + int len; + pimdev_hdr *hdr; + + hdr = malloc(PIM_BUF_SIZE); + if (hdr == NULL) + return; + len = recv(sock, hdr, PIM_BUF_SIZE, 0); + if (len < 0) { + perror("recv"); + free(hdr); + return; + } + if (len < 8) { + printf("handle_pim: too short (%d)\n", len); + free(hdr); + return; + } + + if (hdr->op != (int) htonl(PIMOP_TRAP)) { + free(hdr); + return; + } + switch (ntohl(hdr->oid)) { + case DOT11_OID_PROBE: + prism54_handle_probe(drv, hdr, len); + break; + case DOT11_OID_DEAUTHENTICATEEX: + case DOT11_OID_DEAUTHENTICATE: + prism54_handle_deauth(drv, hdr, len); + break; + case DOT11_OID_DISASSOCIATEEX: + case DOT11_OID_DISASSOCIATE: + prism54_handle_disassoc(drv, hdr, len); + break; + case DOT11_OID_AUTHENTICATEEX: + case DOT11_OID_AUTHENTICATE: + prism54_handle_auth(drv, hdr, len); + break; + case DOT11_OID_ASSOCIATEEX: + case DOT11_OID_REASSOCIATEEX: + case DOT11_OID_ASSOCIATE: + case DOT11_OID_REASSOCIATE: + prism54_handle_assoc(drv, hdr, len); + default: + break; + } + + free(hdr); +} + + +static void handle_802_3(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + ieee802_3_hdr *hdr; + + hdr = malloc(PIM_BUF_SIZE); + if (hdr == NULL) + return; + len = recv(sock, hdr, PIM_BUF_SIZE, 0); + if (len < 0) { + perror("recv"); + free(hdr); + return; + } + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_802_3: too short (%d)", len); + free(hdr); + return; + } + if (hdr->type == htons(ETH_P_PAE)) { + ieee802_1x_receive(hapd, (u8 *) &hdr->sa[0], (u8 *) &hdr[1], + len - sizeof(*hdr)); + } + free(hdr); +} + + +static int prism54_init_sockets(struct prism54_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_802_3, drv->hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + if (hapd->conf->bridge[0] != '\0') { + printf("opening bridge: %s\n", hapd->conf->bridge); + os_strlcpy(ifr.ifr_name, hapd->conf->bridge, + sizeof(ifr.ifr_name)); + } else { + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + } + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + addr.sll_protocol = htons(ETH_P_PAE); + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + drv->pim_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->pim_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->pim_sock, handle_pim, drv, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->pim_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + addr.sll_protocol = htons(ETH_P_ALL); + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->pim_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + return 0; +} + + +static void * prism54_driver_init(struct hostapd_data *hapd) +{ + struct prism54_driver_data *drv; + + drv = os_zalloc(sizeof(struct prism54_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd Prism54 driver " + "data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->pim_sock = drv->sock = -1; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + if (prism54_init_sockets(drv)) { + free(drv); + return NULL; + } + prism54_init_1x(drv); + /* must clean previous elems */ + hostapd_set_generic_elem(hapd, NULL, 0); + + return drv; +} + + +static void prism54_driver_deinit(void *priv) +{ + struct prism54_driver_data *drv = priv; + + if (drv->pim_sock >= 0) + close(drv->pim_sock); + + if (drv->sock >= 0) + close(drv->sock); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_prism54_ops = { + .name = "prism54", + .init = prism54_driver_init, + .deinit = prism54_driver_deinit, + /* .set_ieee8021x = prism54_init_1x, */ + .set_privacy = prism54_set_privacy_invoked, + .set_encryption = prism54_set_encryption, + .get_seqnum = prism54_get_seqnum, + .flush = prism54_flush, + .set_generic_elem = prism54_set_generic_elem, + .send_eapol = prism54_send_eapol, + .sta_set_flags = prism54_sta_set_flags, + .sta_deauth = prism54_sta_deauth, + .sta_disassoc = prism54_sta_disassoc, + .set_ssid = prism54_ioctl_setiwessid, + .get_inact_sec = prism54_get_inact_sec, +}; diff --git a/contrib/hostapd/driver_test.c b/hostapd/driver_test.c index 368d76b061039..9930a8284755f 100644 --- a/contrib/hostapd/driver_test.c +++ b/hostapd/driver_test.c @@ -1,6 +1,6 @@ /* * hostapd / Driver interface for development testing - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,7 +13,6 @@ */ #include "includes.h" -#include <sys/ioctl.h> #include <sys/un.h> #include <dirent.h> @@ -23,13 +22,13 @@ #include "eloop.h" #include "ieee802_1x.h" #include "sta_info.h" -#include "eapol_sm.h" #include "wpa.h" #include "accounting.h" -#include "radius.h" -#include "l2_packet.h" +#include "radius/radius.h" +#include "l2_packet/l2_packet.h" #include "ieee802_11.h" #include "hw_features.h" +#include "wps_hostapd.h" struct test_client_socket { @@ -46,27 +45,31 @@ struct test_driver_bss { u8 bssid[ETH_ALEN]; u8 *ie; size_t ielen; + u8 *wps_beacon_ie; + size_t wps_beacon_ie_len; + u8 *wps_probe_resp_ie; + size_t wps_probe_resp_ie_len; u8 ssid[32]; size_t ssid_len; int privacy; }; struct test_driver_data { - struct driver_ops ops; struct hostapd_data *hapd; struct test_client_socket *cli; int test_socket; struct test_driver_bss *bss; char *socket_dir; char *own_socket_path; + int udp_port; }; -static const struct driver_ops test_driver_ops; - static void test_driver_free_bss(struct test_driver_bss *bss) { free(bss->ie); + free(bss->wps_beacon_ie); + free(bss->wps_probe_resp_ie); free(bss); } @@ -136,7 +139,7 @@ static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, memcpy(eth.h_dest, addr, ETH_ALEN); memcpy(eth.h_source, own_addr, ETH_ALEN); - eth.h_proto = htons(ETH_P_EAPOL); + eth.h_proto = host_to_be16(ETH_P_EAPOL); io[0].iov_base = "EAPOL "; io[0].iov_len = 6; @@ -154,6 +157,93 @@ static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, } +static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + struct test_driver_data *drv = priv; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + char desttxt[30]; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + int ret = 0, broadcast = 0, count = 0; + + if (drv->test_socket < 0 || drv->socket_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " + "socket_dir=%p)", + __func__, drv->test_socket, drv->socket_dir); + return -1; + } + + broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst)); + + memcpy(eth.h_dest, dst, ETH_ALEN); + memcpy(eth.h_source, src, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + io[0].iov_base = "ETHER "; + io[0].iov_len = 6; + io[1].iov_base = ð + io[1].iov_len = sizeof(eth); + io[2].iov_base = (u8 *) data; + io[2].iov_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + + dir = opendir(drv->socket_dir); + if (dir == NULL) { + perror("test_driver: opendir"); + return -1; + } + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. Also accept + * DT_UNKNOWN (0) in case the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + drv->socket_dir, dent->d_name); + + if (strcmp(addr.sun_path, drv->own_socket_path) == 0) + continue; + if (!broadcast && strstr(dent->d_name, desttxt) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg"); + count++; + } + closedir(dir); + + if (!broadcast && count == 0) { + wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found", + __func__, MAC2STR(dst)); + return -1; + } + + return ret; +} + + static int test_driver_send_mgmt_frame(void *priv, const void *buf, size_t len, int flags) { @@ -239,14 +329,44 @@ static int test_driver_send_mgmt_frame(void *priv, const void *buf, static void test_driver_scan(struct test_driver_data *drv, - struct sockaddr_un *from, socklen_t fromlen) + struct sockaddr_un *from, socklen_t fromlen, + char *data) { char buf[512], *pos, *end; int ret; struct test_driver_bss *bss; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + + /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */ wpa_printf(MSG_DEBUG, "test_driver: SCAN"); + if (*data) { + if (*data != ' ' || + hwaddr_aton(data + 1, sa)) { + wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN " + "command format"); + return; + } + + data += 18; + while (*data == ' ') + data++; + ielen = os_strlen(data) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(data, ie, ielen) < 0) + ielen = 0; + + wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen); + + hostapd_wps_probe_req_rx(drv->hapd, sa, ie, ielen); + } + for (bss = drv->bss; bss; bss = bss->next) { pos = buf; end = buf + sizeof(buf); @@ -264,6 +384,8 @@ static void test_driver_scan(struct test_driver_data *drv, return; pos += ret; pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); + pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie, + bss->wps_probe_resp_ie_len); if (bss->privacy) { ret = snprintf(pos, end - pos, " PRIVACY"); @@ -328,13 +450,24 @@ static int test_driver_new_sta(struct test_driver_data *drv, if (sta == NULL) return -1; } - accounting_sta_get_id(hapd, sta); + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { + if (hapd->conf->wps_state) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + printf("test_driver: no IE from STA\n"); return -1; } + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); @@ -344,13 +477,15 @@ static int test_driver_new_sta(struct test_driver_data *drv, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie, ielen); + ie, ielen, NULL, 0); if (res != WPA_IE_OK) { printf("WPA/RSN information element rejected? " "(res %u)\n", res); + wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); return -1; } } +skip_wpa_check: new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; @@ -376,7 +511,7 @@ static void test_driver_assoc(struct test_driver_data *drv, /* data: STA-addr SSID(hex) IEs(hex) */ - cli = wpa_zalloc(sizeof(*cli)); + cli = os_zalloc(sizeof(*cli)); if (cli == NULL) return; @@ -472,12 +607,11 @@ static void test_driver_eapol(struct test_driver_data *drv, { struct test_client_socket *cli; if (datalen > 14) { - u8 *proto = data + 2 * ETH_ALEN; /* Skip Ethernet header */ wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" MACSTR " proto=%04x", MAC2STR(data), MAC2STR(data + ETH_ALEN), - (proto[0] << 8) | proto[1]); + WPA_GET_BE16(data + 2 * ETH_ALEN)); data += 14; datalen -= 14; } @@ -495,6 +629,30 @@ static void test_driver_eapol(struct test_driver_data *drv, } +static void test_driver_ether(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct l2_ethhdr *eth; + + if (datalen < sizeof(*eth)) + return; + + eth = (struct l2_ethhdr *) data; + wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src=" + MACSTR " proto=%04x", + MAC2STR(eth->h_dest), MAC2STR(eth->h_source), + be_to_host16(eth->h_proto)); + +#ifdef CONFIG_IEEE80211R + if (be_to_host16(eth->h_proto) == ETH_P_RRB) { + wpa_ft_rrb_rx(drv->hapd->wpa_auth, eth->h_source, + data + sizeof(*eth), datalen - sizeof(*eth)); + } +#endif /* CONFIG_IEEE80211R */ +} + + static void test_driver_mlme(struct test_driver_data *drv, struct sockaddr_un *from, socklen_t fromlen, u8 *data, size_t datalen) @@ -506,7 +664,7 @@ static void test_driver_mlme(struct test_driver_data *drv, if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { struct test_client_socket *cli; - cli = wpa_zalloc(sizeof(*cli)); + cli = os_zalloc(sizeof(*cli)); if (cli == NULL) return; wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, @@ -548,8 +706,8 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); - if (strcmp(buf, "SCAN") == 0) { - test_driver_scan(drv, &from, fromlen); + if (strncmp(buf, "SCAN", 4) == 0) { + test_driver_scan(drv, &from, fromlen, buf + 4); } else if (strncmp(buf, "ASSOC ", 6) == 0) { test_driver_assoc(drv, &from, fromlen, buf + 6); } else if (strcmp(buf, "DISASSOC") == 0) { @@ -557,6 +715,9 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) } else if (strncmp(buf, "EAPOL ", 6) == 0) { test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, res - 6); + } else if (strncmp(buf, "ETHER ", 6) == 0) { + test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); } else if (strncmp(buf, "MLME ", 5) == 0) { test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); } else { @@ -566,36 +727,108 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) } +static struct test_driver_bss * +test_driver_get_bss(struct test_driver_data *drv, const char *ifname) +{ + struct test_driver_bss *bss; + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) == 0) + return bss; + } + return NULL; +} + + static int test_driver_set_generic_elem(const char *ifname, void *priv, const u8 *elem, size_t elem_len) { struct test_driver_data *drv = priv; struct test_driver_bss *bss; - for (bss = drv->bss; bss; bss = bss->next) { - if (strcmp(bss->ifname, ifname) != 0) - continue; + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; - free(bss->ie); + free(bss->ie); - if (elem == NULL) { - bss->ie = NULL; - bss->ielen = 0; - return 0; - } + if (elem == NULL) { + bss->ie = NULL; + bss->ielen = 0; + return 0; + } - bss->ie = malloc(elem_len); - if (bss->ie) { - memcpy(bss->ie, elem, elem_len); - bss->ielen = elem_len; - return 0; - } else { - bss->ielen = 0; - return -1; - } + bss->ie = malloc(elem_len); + if (bss->ie == NULL) { + bss->ielen = 0; + return -1; } - return -1; + memcpy(bss->ie, elem, elem_len); + bss->ielen = elem_len; + return 0; +} + + +static int test_driver_set_wps_beacon_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_hexdump(MSG_DEBUG, "test_driver: Beacon WPS IE", ie, len); + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; + + free(bss->wps_beacon_ie); + + if (ie == NULL) { + bss->wps_beacon_ie = NULL; + bss->wps_beacon_ie_len = 0; + return 0; + } + + bss->wps_beacon_ie = malloc(len); + if (bss->wps_beacon_ie == NULL) { + bss->wps_beacon_ie_len = 0; + return -1; + } + + memcpy(bss->wps_beacon_ie, ie, len); + bss->wps_beacon_ie_len = len; + return 0; +} + + +static int test_driver_set_wps_probe_resp_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_hexdump(MSG_DEBUG, "test_driver: ProbeResp WPS IE", ie, len); + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; + + free(bss->wps_probe_resp_ie); + + if (ie == NULL) { + bss->wps_probe_resp_ie = NULL; + bss->wps_probe_resp_ie_len = 0; + return 0; + } + + bss->wps_probe_resp_ie = malloc(len); + if (bss->wps_probe_resp_ie == NULL) { + bss->wps_probe_resp_ie_len = 0; + return -1; + } + + memcpy(bss->wps_probe_resp_ie, ie, len); + bss->wps_probe_resp_ie_len = len; + return 0; } @@ -652,22 +885,21 @@ test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) *num_modes = 3; *flags = 0; - modes = wpa_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); if (modes == NULL) return NULL; modes[0].mode = HOSTAPD_MODE_IEEE80211G; modes[0].num_channels = 1; modes[0].num_rates = 1; - modes[0].channels = wpa_zalloc(sizeof(struct hostapd_channel_data)); - modes[0].rates = wpa_zalloc(sizeof(struct hostapd_rate_data)); + modes[0].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[0].rates = os_zalloc(sizeof(struct hostapd_rate_data)); if (modes[0].channels == NULL || modes[0].rates == NULL) { hostapd_free_hw_features(modes, *num_modes); return NULL; } modes[0].channels[0].chan = 1; modes[0].channels[0].freq = 2412; - modes[0].channels[0].flag = HOSTAPD_CHAN_W_SCAN | - HOSTAPD_CHAN_W_ACTIVE_SCAN; + modes[0].channels[0].flag = 0; modes[0].rates[0].rate = 10; modes[0].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; @@ -675,16 +907,15 @@ test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[1].mode = HOSTAPD_MODE_IEEE80211B; modes[1].num_channels = 1; modes[1].num_rates = 1; - modes[1].channels = wpa_zalloc(sizeof(struct hostapd_channel_data)); - modes[1].rates = wpa_zalloc(sizeof(struct hostapd_rate_data)); + modes[1].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[1].rates = os_zalloc(sizeof(struct hostapd_rate_data)); if (modes[1].channels == NULL || modes[1].rates == NULL) { hostapd_free_hw_features(modes, *num_modes); return NULL; } modes[1].channels[0].chan = 1; modes[1].channels[0].freq = 2412; - modes[1].channels[0].flag = HOSTAPD_CHAN_W_SCAN | - HOSTAPD_CHAN_W_ACTIVE_SCAN; + modes[1].channels[0].flag = 0; modes[1].rates[0].rate = 10; modes[1].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; @@ -692,16 +923,15 @@ test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[2].mode = HOSTAPD_MODE_IEEE80211A; modes[2].num_channels = 1; modes[2].num_rates = 1; - modes[2].channels = wpa_zalloc(sizeof(struct hostapd_channel_data)); - modes[2].rates = wpa_zalloc(sizeof(struct hostapd_rate_data)); + modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[2].rates = os_zalloc(sizeof(struct hostapd_rate_data)); if (modes[2].channels == NULL || modes[2].rates == NULL) { hostapd_free_hw_features(modes, *num_modes); return NULL; } modes[2].channels[0].chan = 60; modes[2].channels[0].freq = 5300; - modes[2].channels[0].flag = HOSTAPD_CHAN_W_SCAN | - HOSTAPD_CHAN_W_ACTIVE_SCAN; + modes[2].channels[0].flag = 0; modes[2].rates[0].rate = 60; modes[2].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | HOSTAPD_RATE_MANDATORY; @@ -718,11 +948,11 @@ static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid) wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", __func__, ifname, MAC2STR(bssid)); - bss = wpa_zalloc(sizeof(*bss)); + bss = os_zalloc(sizeof(*bss)); if (bss == NULL) return -1; - strncpy(bss->ifname, ifname, IFNAMSIZ); + os_strlcpy(bss->ifname, ifname, IFNAMSIZ); memcpy(bss->bssid, bssid, ETH_ALEN); bss->next = drv->bss; @@ -874,15 +1104,17 @@ static int test_driver_set_sta_vlan(void *priv, const u8 *addr, static int test_driver_sta_add(const char *ifname, void *priv, const u8 *addr, u16 aid, u16 capability, u8 *supp_rates, - size_t supp_rates_len, int flags) + size_t supp_rates_len, int flags, + u16 listen_interval) { struct test_driver_data *drv = priv; struct test_client_socket *cli; struct test_driver_bss *bss; wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " - "capability=0x%x flags=0x%x", - __func__, ifname, MAC2STR(addr), aid, capability, flags); + "capability=0x%x flags=0x%x listen_interval=%d)", + __func__, ifname, MAC2STR(addr), aid, capability, flags, + listen_interval); wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", supp_rates, supp_rates_len); @@ -914,24 +1146,26 @@ static int test_driver_sta_add(const char *ifname, void *priv, const u8 *addr, } -static int test_driver_init(struct hostapd_data *hapd) +static void * test_driver_init(struct hostapd_data *hapd) { struct test_driver_data *drv; - struct sockaddr_un addr; + struct sockaddr_un addr_un; + struct sockaddr_in addr_in; + struct sockaddr *addr; + socklen_t alen; - drv = wpa_zalloc(sizeof(struct test_driver_data)); + drv = os_zalloc(sizeof(struct test_driver_data)); if (drv == NULL) { printf("Could not allocate memory for test driver data\n"); - return -1; + return NULL; } - drv->bss = wpa_zalloc(sizeof(*drv->bss)); + drv->bss = os_zalloc(sizeof(*drv->bss)); if (drv->bss == NULL) { printf("Could not allocate memory for test driver BSS data\n"); free(drv); - return -1; + return NULL; } - drv->ops = test_driver_ops; drv->hapd = hapd; /* Generate a MAC address to help testing with multiple APs */ @@ -941,14 +1175,15 @@ static int test_driver_init(struct hostapd_data *hapd) (const u8 *) hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, hapd->own_addr + 1, ETH_ALEN - 1); - strncpy(drv->bss->ifname, hapd->conf->iface, IFNAMSIZ); + os_strlcpy(drv->bss->ifname, hapd->conf->iface, IFNAMSIZ); memcpy(drv->bss->bssid, hapd->own_addr, ETH_ALEN); if (hapd->conf->test_socket) { - if (strlen(hapd->conf->test_socket) >= sizeof(addr.sun_path)) { + if (strlen(hapd->conf->test_socket) >= + sizeof(addr_un.sun_path)) { printf("Too long test_socket path\n"); test_driver_free_priv(drv); - return -1; + return NULL; } if (strncmp(hapd->conf->test_socket, "DIR:", 4) == 0) { size_t len = strlen(hapd->conf->test_socket) + 30; @@ -960,40 +1195,52 @@ static int test_driver_init(struct hostapd_data *hapd) hapd->conf->test_socket + 4, MAC2STR(hapd->own_addr)); } + } else if (strncmp(hapd->conf->test_socket, "UDP:", 4) == 0) { + drv->udp_port = atoi(hapd->conf->test_socket + 4); } else { drv->own_socket_path = strdup(hapd->conf->test_socket); } - if (drv->own_socket_path == NULL) { + if (drv->own_socket_path == NULL && drv->udp_port == 0) { test_driver_free_priv(drv); - return -1; + return NULL; } - drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, + SOCK_DGRAM, 0); if (drv->test_socket < 0) { - perror("socket(PF_UNIX)"); + perror("socket"); test_driver_free_priv(drv); - return -1; + return NULL; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, drv->own_socket_path, - sizeof(addr.sun_path)); - if (bind(drv->test_socket, (struct sockaddr *) &addr, - sizeof(addr)) < 0) { + if (drv->udp_port) { + os_memset(&addr_in, 0, sizeof(addr_in)); + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(drv->udp_port); + addr = (struct sockaddr *) &addr_in; + alen = sizeof(addr_in); + } else { + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_strlcpy(addr_un.sun_path, drv->own_socket_path, + sizeof(addr_un.sun_path)); + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); + } + if (bind(drv->test_socket, addr, alen) < 0) { perror("bind(PF_UNIX)"); close(drv->test_socket); - unlink(drv->own_socket_path); + if (drv->own_socket_path) + unlink(drv->own_socket_path); test_driver_free_priv(drv); - return -1; + return NULL; } eloop_register_read_sock(drv->test_socket, test_driver_receive_unix, drv, NULL); } else drv->test_socket = -1; - hapd->driver = &drv->ops; - return 0; + return drv; } @@ -1012,11 +1259,10 @@ static void test_driver_deinit(void *priv) if (drv->test_socket >= 0) { eloop_unregister_read_sock(drv->test_socket); close(drv->test_socket); - unlink(drv->own_socket_path); + if (drv->own_socket_path) + unlink(drv->own_socket_path); } - drv->hapd->driver = NULL; - /* There should be only one BSS remaining at this point. */ if (drv->bss == NULL) wpa_printf(MSG_ERROR, "%s: drv->bss == NULL", __func__); @@ -1027,7 +1273,7 @@ static void test_driver_deinit(void *priv) } -static const struct driver_ops test_driver_ops = { +const struct wpa_driver_ops wpa_driver_test_ops = { .name = "test", .init = test_driver_init, .deinit = test_driver_deinit, @@ -1048,10 +1294,7 @@ static const struct driver_ops test_driver_ops = { .set_encryption = test_driver_set_encryption, .set_sta_vlan = test_driver_set_sta_vlan, .sta_add = test_driver_sta_add, + .send_ether = test_driver_send_ether, + .set_wps_beacon_ie = test_driver_set_wps_beacon_ie, + .set_wps_probe_resp_ie = test_driver_set_wps_probe_resp_ie, }; - - -void test_driver_register(void) -{ - driver_register(test_driver_ops.name, &test_driver_ops); -} diff --git a/hostapd/driver_wired.c b/hostapd/driver_wired.c new file mode 100644 index 0000000000000..61cb667cca7f6 --- /dev/null +++ b/hostapd/driver_wired.c @@ -0,0 +1,372 @@ +/* + * hostapd / Kernel driver communication for wired (Ethernet) drivers + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#ifdef USE_KERNEL_HEADERS +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/if.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <net/if.h> +#include <netpacket/packet.h> +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "driver.h" +#include "accounting.h" + + +struct wired_driver_data { + struct hostapd_data *hapd; + + int sock; /* raw packet socket for driver access */ + int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; +}; + + +#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03} + + +/* TODO: detecting new devices should eventually be changed from using DHCP + * snooping to trigger on any packet from a new layer 2 MAC address, e.g., + * based on ebtables, etc. */ + +struct dhcp_message { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + + +static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return; + + wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR + " - adding a new STA", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta) { + hostapd_new_assoc_sta(hapd, sta, 0); + } else { + wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR, + MAC2STR(addr)); + } +} + + +static void handle_data(struct hostapd_data *hapd, unsigned char *buf, + size_t len) +{ + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + wired_possible_new_sta(hapd, sa); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + ieee802_1x_receive(hapd, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_data(hapd, buf, len); +} + + +static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + struct dhcp_message *msg; + u8 *mac_address; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + /* must contain at least dhcp_message->chaddr */ + if (len < 44) { + wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len); + return; + } + + msg = (struct dhcp_message *) buf; + mac_address = (u8 *) &(msg->chaddr); + + wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR, + MAC2STR(mac_address)); + + wired_possible_new_sta(hapd, mac_address); +} + + +static int wired_init_sockets(struct wired_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct ifreq ifr; + struct sockaddr_ll addr; + struct sockaddr_in addr2; + struct packet_mreq mreq; + u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP; + int n = 1; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, hapd->conf->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + /* filter multicast address */ + memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifr.ifr_ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = 6; + memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen); + + if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, hapd->conf->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* setup dhcp listen socket for sta detection */ + if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket call failed for dhcp"); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&addr2, 0, sizeof(addr2)); + addr2.sin_family = AF_INET; + addr2.sin_port = htons(67); + addr2.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ); + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *) &ifr, sizeof(ifr)) < 0) { + perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + perror("bind"); + return -1; + } + + return 0; +} + + +static int wired_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct wired_driver_data *drv = priv; + u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for wired_send_eapol(len=%lu)\n", + (unsigned long) len); + return -1; + } + + memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + memcpy(pos, data, data_len); + + res = send(drv->sock, (u8 *) hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("wired_send_eapol: send"); + printf("wired_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static void * wired_driver_init(struct hostapd_data *hapd) +{ + struct wired_driver_data *drv; + + drv = os_zalloc(sizeof(struct wired_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for wired driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; + + if (wired_init_sockets(drv)) { + free(drv); + return NULL; + } + + return drv; +} + + +static void wired_driver_deinit(void *priv) +{ + struct wired_driver_data *drv = priv; + + if (drv->sock >= 0) + close(drv->sock); + + if (drv->dhcp_sock >= 0) + close(drv->dhcp_sock); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .init = wired_driver_init, + .deinit = wired_driver_deinit, + .send_eapol = wired_send_eapol, +}; diff --git a/hostapd/drivers.c b/hostapd/drivers.c new file mode 100644 index 0000000000000..30061904c8762 --- /dev/null +++ b/hostapd/drivers.c @@ -0,0 +1,71 @@ +/* + * hostapd / driver interface list + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + + +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_NL80211 +extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_PRISM54 +extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_WIRED +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST +extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_NONE +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ + + +struct wpa_driver_ops *hostapd_drivers[] = +{ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_NL80211 + &wpa_driver_nl80211_ops, +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_PRISM54 + &wpa_driver_prism54_ops, +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_WIRED + &wpa_driver_wired_ops, +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST + &wpa_driver_test_ops, +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_NONE + &wpa_driver_none_ops, +#endif /* CONFIG_DRIVER_NONE */ + NULL +}; diff --git a/hostapd/eap_testing.txt b/hostapd/eap_testing.txt new file mode 100644 index 0000000000000..04468c39f01ec --- /dev/null +++ b/hostapd/eap_testing.txt @@ -0,0 +1,77 @@ +Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication + +Test matrix + ++) tested successfully +F) failed +-) peer did not support +?) not tested + +XSupplicant --------------------------------. +Intel PROSet ---------------------------. | +Windows XP -------------------------. | | +Mac OS X 10.4 ------------------. | | | +Nokia S60 ------------------. | | | | +wpa_supplicant ---------. | | | | | + | | | | | | + +EAP-MD5 + - ? ? - +EAP-GTC + - ? - - +EAP-MSCHAPv2 + - ? - - +EAP-TLS + + +1 + + +EAP-PEAPv0/MSCHAPv2 + + + + + + +EAP-PEAPv0/GTC + + + - + +EAP-PEAPv0/MD5 + - + - - +EAP-PEAPv0/TLS + F - + + +EAP-PEAPv0/SIM + + - - - +EAP-PEAPv0/AKA + + - - - +EAP-PEAPv0/PSK + - - - - +EAP-PEAPv0/PAX + - - - - +EAP-PEAPv0/SAKE + - - - - +EAP-PEAPv0/GPSK + - - - - +EAP-PEAPv1/MSCHAPv2 + + + - + + +EAP-PEAPv1/GTC + + + - + +EAP-PEAPv1/MD5 + - + - - +EAP-PEAPv1/TLS + F - - + +EAP-PEAPv1/SIM + + - - - +EAP-PEAPv1/AKA + + - - - +EAP-PEAPv1/PSK + - - - - +EAP-PEAPv1/PAX + - - - - +EAP-PEAPv1/SAKE + - - - - +EAP-PEAPv1/GPSK + - - - - +EAP-TTLS/CHAP + - + - + + +EAP-TTLS/MSCHAP + - + - + + +EAP-TTLS/MSCHAPv2 + + + - + + +EAP-TTLS/PAP + - + - + + +EAP-TTLS/EAP-MD5 + - - - - + +EAP-TTLS/EAP-GTC + + - - - +EAP-TTLS/EAP-MSCHAPv2 + + - - - +EAP-TTLS/EAP-TLS + F - - - +EAP-TTLS/EAP-SIM + + - - - +EAP-TTLS/EAP-AKA + + - - - +EAP-TTLS + TNC + - - - - +EAP-SIM + + - - + +EAP-AKA + + - - - +EAP-PAX + - - - - +EAP-SAKE + - - - - +EAP-GPSK + - - - - +EAP-FAST/MSCHAPv2(prov) + - F - F +EAP-FAST/GTC(auth) + - + - + +EAP-FAST/MSCHAPv2(aprov)+ - F - F +EAP-FAST/GTC(aprov) + - F - F +EAP-FAST/MD5(aprov) + - - - - +EAP-FAST/TLS(aprov) + - - - - +EAP-FAST/SIM(aprov) + - - - - +EAP-FAST/AKA(aprov) + - - - - +EAP-FAST/MSCHAPv2(auth) + - + - + +EAP-FAST/MD5(auth) + - + - - +EAP-FAST/TLS(auth) + - - - - +EAP-FAST/SIM(auth) + - - - - +EAP-FAST/AKA(auth) + - - - - +EAP-FAST + TNC + - - - - +EAP-IKEv2 + - - - - +EAP-TNC + - - - - + +1) EAP-TLS itself worked, but peer certificate validation failed at + least when using the internal TLS server (peer included incorrect + certificates in the chain?) diff --git a/contrib/hostapd/eapol_sm.c b/hostapd/eapol_sm.c index f4f575298e9f3..8e9d56c7964fb 100644 --- a/contrib/hostapd/eapol_sm.c +++ b/hostapd/eapol_sm.c @@ -1,6 +1,6 @@ /* - * hostapd / IEEE 802.1X Authenticator - EAPOL state machine - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,8 +21,9 @@ #include "wpa.h" #include "preauth.h" #include "sta_info.h" -#include "eap.h" +#include "eap_server/eap.h" #include "state_machine.h" +#include "eap_common/eap_common.h" #define STATE_MACHINE_DATA struct eapol_state_machine #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" @@ -30,20 +31,19 @@ static struct eapol_callbacks eapol_cb; -/* EAPOL state machines are described in IEEE Std 802.1X-REV-d11, Chap. 8.2 */ +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ #define setPortAuthorized() \ -ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1) +sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 1) #define setPortUnauthorized() \ -ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0) +sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 0) /* procedures */ -#define txCannedFail() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 0) -#define txCannedSuccess() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 1) -#define txReq() ieee802_1x_tx_req(sm->hapd, sm->sta) -#define sendRespToServer() ieee802_1x_send_resp_to_server(sm->hapd, sm->sta) -#define abortAuth() ieee802_1x_abort_auth(sm->hapd, sm->sta) -#define txKey() ieee802_1x_tx_key(sm->hapd, sm->sta) +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) +#define txReq() eapol_auth_tx_req(sm) +#define abortAuth() sm->eapol->cb.abort_auth(sm->hapd, sm->sta) +#define txKey() sm->eapol->cb.tx_key(sm->hapd, sm->sta) #define processKey() do { } while (0) @@ -51,9 +51,103 @@ static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); -/* Port Timers state machine - implemented as a function that will be called - * once a second as a registered event loop timeout */ +static void eapol_auth_logger(struct eapol_authenticator *eapol, + const u8 *addr, logger_level level, + const char *txt) +{ + if (eapol->cb.logger == NULL) + return; + eapol->cb.logger(eapol->conf.hapd, addr, level, txt); +} + + +static void eapol_auth_vlogger(struct eapol_authenticator *eapol, + const u8 *addr, logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (eapol->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + eapol_auth_logger(eapol, addr, level, format); + + os_free(format); +} + + +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, + int success) +{ + struct eap_hdr eap; + + os_memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = ++sm->last_eap_id; + eap.length = host_to_be16(sizeof(eap)); + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending canned EAP packet %s (identifier %d)", + success ? "SUCCESS" : "FAILURE", eap.identifier); + sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + (u8 *) &eap, sizeof(eap)); + sm->dot1xAuthEapolFramesTx++; +} + + +static void eapol_auth_tx_req(struct eapol_state_machine *sm) +{ + if (sm->eap_if->eapReqData == NULL || + wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { + eapol_auth_logger(sm->eapol, sm->addr, + EAPOL_LOGGER_DEBUG, + "TxReq called, but there is no EAP request " + "from authentication server"); + return; + } + + if (sm->flags & EAPOL_SM_WAIT_START) { + wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR + " while waiting for EAPOL-Start", + MAC2STR(sm->addr)); + return; + } + + sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending EAP Packet (identifier %d)", + sm->last_eap_id); + sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(sm->eap_if->eapReqData), + wpabuf_len(sm->eap_if->eapReqData)); + sm->dot1xAuthEapolFramesTx++; + if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + +/** + * eapol_port_timers_tick - Port Timers state machine + * @eloop_ctx: struct eapol_state_machine * + * @timeout_ctx: Not used + * + * This statemachine is implemented as a function that will be called + * once a second as a registered event loop timeout. + */ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) { struct eapol_state_machine *state = timeout_ctx; @@ -85,6 +179,15 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) } } + if (state->eap_if->retransWhile > 0) { + state->eap_if->retransWhile--; + if (state->eap_if->retransWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - (EAP) retransWhile --> 0", + MAC2STR(state->addr)); + } + } + eapol_sm_step_run(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); @@ -98,8 +201,6 @@ SM_STATE(AUTH_PAE, INITIALIZE) { SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; - - sm->currentId = 255; } @@ -121,10 +222,8 @@ SM_STATE(AUTH_PAE, DISCONNECTED) sm->reAuthCount = 0; sm->eapolLogoff = FALSE; if (!from_initialize) { - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 0); - else - ieee802_1x_finished(sm->hapd, sm->sta, 0); + sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); } } @@ -142,8 +241,7 @@ SM_STATE(AUTH_PAE, RESTART) SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); - sm->eapRestart = TRUE; - ieee802_1x_request_identity(sm->hapd, sm->sta); + sm->eap_if->eapRestart = TRUE; } @@ -171,22 +269,18 @@ SM_STATE(AUTH_PAE, HELD) sm->quietWhile = sm->quietPeriod; sm->eapolLogoff = FALSE; - hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "authentication failed - " - "EAP type: %d (%s)", - sm->eap_type_authsrv, - eap_type_text(sm->eap_type_authsrv)); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, + "authentication failed - EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv)); if (sm->eap_type_authsrv != sm->eap_type_supp) { - hostapd_logger(sm->hapd, sm->addr, - HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, - "Supplicant used different EAP type: %d (%s)", - sm->eap_type_supp, - eap_type_text(sm->eap_type_supp)); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "Supplicant used different EAP type: " + "%d (%s)", sm->eap_type_supp, + eap_type_text(sm->eap_type_supp)); } - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 0); - else - ieee802_1x_finished(sm->hapd, sm->sta, 0); + sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); } @@ -206,24 +300,17 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) extra = " (pre-authentication)"; else if (wpa_auth_sta_get_pmksa(sm->sta->wpa_sm)) extra = " (PMKSA cache)"; - hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authenticated - EAP type: %d (%s)" - "%s", sm->eap_type_authsrv, - eap_type_text(sm->eap_type_authsrv), extra); - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 1); - else - ieee802_1x_finished(sm->hapd, sm->sta, 1); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "authenticated - EAP type: %d (%s)%s", + sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv), extra); + sm->eapol->cb.finished(sm->hapd, sm->sta, 1, + sm->flags & EAPOL_SM_PREAUTH); } SM_STATE(AUTH_PAE, AUTHENTICATING) { - if (sm->auth_pae_state == AUTH_PAE_CONNECTING && sm->rx_identity) { - sm->authEntersAuthenticating++; - sm->rx_identity = FALSE; - } - SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); sm->eapolStart = FALSE; @@ -282,16 +369,16 @@ SM_STATE(AUTH_PAE, FORCE_UNAUTH) SM_STEP(AUTH_PAE) { if ((sm->portControl == Auto && sm->portMode != sm->portControl) || - sm->initialize || !sm->portEnabled) - SM_ENTER(AUTH_PAE, INITIALIZE); + sm->initialize || !sm->eap_if->portEnabled) + SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && sm->portMode != sm->portControl && - !(sm->initialize || !sm->portEnabled)) - SM_ENTER(AUTH_PAE, FORCE_AUTH); + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && sm->portMode != sm->portControl && - !(sm->initialize || !sm->portEnabled)) - SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); else { switch (sm->auth_pae_state) { case AUTH_PAE_INITIALIZE: @@ -301,7 +388,7 @@ SM_STEP(AUTH_PAE) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_RESTART: - if (!sm->eapRestart) + if (!sm->eap_if->eapRestart) SM_ENTER(AUTH_PAE, CONNECTING); break; case AUTH_PAE_HELD: @@ -311,9 +398,9 @@ SM_STEP(AUTH_PAE) case AUTH_PAE_CONNECTING: if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); - else if ((sm->eapReq && + else if ((sm->eap_if->eapReq && sm->reAuthCount <= sm->reAuthMax) || - sm->eapSuccess || sm->eapFail) + sm->eap_if->eapSuccess || sm->eap_if->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: @@ -359,7 +446,7 @@ SM_STATE(BE_AUTH, INITIALIZE) SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); abortAuth(); - sm->eapNoReq = FALSE; + sm->eap_if->eapNoReq = FALSE; sm->authAbort = FALSE; } @@ -369,7 +456,7 @@ SM_STATE(BE_AUTH, REQUEST) SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); txReq(); - sm->eapReq = FALSE; + sm->eap_if->eapReq = FALSE; sm->backendOtherRequestsToSupplicant++; /* @@ -393,10 +480,10 @@ SM_STATE(BE_AUTH, RESPONSE) sm->authTimeout = FALSE; sm->eapolEap = FALSE; - sm->eapNoReq = FALSE; + sm->eap_if->eapNoReq = FALSE; sm->aWhile = sm->serverTimeout; - sm->eapResp = TRUE; - sendRespToServer(); + sm->eap_if->eapResp = TRUE; + /* sendRespToServer(); */ sm->backendResponses++; } @@ -415,14 +502,7 @@ SM_STATE(BE_AUTH, FAIL) { SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); - /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here. - * txCannelFail() is used as a workaround for the case where - * authentication server does not include EAP-Message with - * Access-Reject. */ - if (sm->last_eap_radius == NULL) - txCannedFail(); - else - txReq(); + txReq(); sm->authFail = TRUE; } @@ -447,14 +527,14 @@ SM_STATE(BE_AUTH, IGNORE) { SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); - sm->eapNoReq = FALSE; + sm->eap_if->eapNoReq = FALSE; } SM_STEP(BE_AUTH) { if (sm->portControl != Auto || sm->initialize || sm->authAbort) { - SM_ENTER(BE_AUTH, INITIALIZE); + SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); return; } @@ -465,23 +545,23 @@ SM_STEP(BE_AUTH) case BE_AUTH_REQUEST: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->eapReq) + else if (sm->eap_if->eapReq) SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapTimeout) + else if (sm->eap_if->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: - if (sm->eapNoReq) + if (sm->eap_if->eapNoReq) SM_ENTER(BE_AUTH, IGNORE); - if (sm->eapReq) { + if (sm->eap_if->eapReq) { sm->backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); - else if (sm->eapFail) { + else if (sm->eap_if->eapFail) { sm->backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); - } else if (sm->eapSuccess) { + } else if (sm->eap_if->eapSuccess) { sm->backendAuthSuccesses++; SM_ENTER(BE_AUTH, SUCCESS); } @@ -496,19 +576,19 @@ SM_STEP(BE_AUTH) SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_IDLE: - if (sm->eapFail && sm->authStart) + if (sm->eap_if->eapFail && sm->authStart) SM_ENTER(BE_AUTH, FAIL); - else if (sm->eapReq && sm->authStart) + else if (sm->eap_if->eapReq && sm->authStart) SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapSuccess && sm->authStart) + else if (sm->eap_if->eapSuccess && sm->authStart) SM_ENTER(BE_AUTH, SUCCESS); break; case BE_AUTH_IGNORE: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->eapReq) + else if (sm->eap_if->eapReq) SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapTimeout) + else if (sm->eap_if->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; } @@ -539,7 +619,7 @@ SM_STEP(REAUTH_TIMER) { if (sm->portControl != Auto || sm->initialize || sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { - SM_ENTER(REAUTH_TIMER, INITIALIZE); + SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); return; } @@ -569,7 +649,7 @@ SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); txKey(); - sm->keyAvailable = FALSE; + sm->eap_if->eapKeyAvailable = FALSE; sm->keyDone = TRUE; } @@ -577,20 +657,20 @@ SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) SM_STEP(AUTH_KEY_TX) { if (sm->initialize || sm->portControl != Auto) { - SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); return; } switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: - if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && - !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) + if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && + sm->keyRun && !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: if (!sm->keyTxEnabled || !sm->keyRun) SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); - else if (sm->keyAvailable) + else if (sm->eap_if->eapKeyAvailable) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; } @@ -617,8 +697,8 @@ SM_STATE(KEY_RX, KEY_RECEIVE) SM_STEP(KEY_RX) { - if (sm->initialize || !sm->portEnabled) { - SM_ENTER(KEY_RX, NO_KEY_RECEIVE); + if (sm->initialize || !sm->eap_if->portEnabled) { + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); return; } @@ -655,20 +735,20 @@ SM_STATE(CTRL_DIR, IN_OR_BOTH) SM_STEP(CTRL_DIR) { if (sm->initialize) { - SM_ENTER(CTRL_DIR, IN_OR_BOTH); + SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); return; } switch (sm->ctrl_dir_state) { case CTRL_DIR_FORCE_BOTH: - if (sm->portEnabled && sm->operEdge) + if (sm->eap_if->portEnabled && sm->operEdge) SM_ENTER(CTRL_DIR, IN_OR_BOTH); break; case CTRL_DIR_IN_OR_BOTH: if (sm->operControlledDirections != sm->adminControlledDirections) SM_ENTER(CTRL_DIR, IN_OR_BOTH); - if (!sm->portEnabled || !sm->operEdge) + if (!sm->eap_if->portEnabled || !sm->operEdge) SM_ENTER(CTRL_DIR, FORCE_BOTH); break; } @@ -677,21 +757,30 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * -eapol_sm_alloc(struct hostapd_data *hapd, struct sta_info *sta) +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int preauth, struct sta_info *sta) { struct eapol_state_machine *sm; + struct hostapd_data *hapd; /* TODO: to be removed */ + struct eap_config eap_conf; - sm = wpa_zalloc(sizeof(*sm)); + if (eapol == NULL) + return NULL; + hapd = eapol->conf.hapd; + + sm = os_zalloc(sizeof(*sm)); if (sm == NULL) { - printf("IEEE 802.1X port state allocation failed\n"); + wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " + "failed"); return NULL; } sm->radius_identifier = -1; - memcpy(sm->addr, sta->addr, ETH_ALEN); - if (sta->flags & WLAN_STA_PREAUTH) + os_memcpy(sm->addr, addr, ETH_ALEN); + if (preauth) sm->flags |= EAPOL_SM_PREAUTH; sm->hapd = hapd; + sm->eapol = eapol; sm->sta = sta; /* Set default values for state machine constants */ @@ -703,8 +792,8 @@ eapol_sm_alloc(struct hostapd_data *hapd, struct sta_info *sta) sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; - sm->reAuthPeriod = hapd->conf->eap_reauth_period; - sm->reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? TRUE : FALSE; + sm->reAuthPeriod = eapol->conf.eap_reauth_period; + sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; @@ -712,70 +801,75 @@ eapol_sm_alloc(struct hostapd_data *hapd, struct sta_info *sta) sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; - sm->portEnabled = FALSE; sm->portControl = Auto; - sm->keyAvailable = FALSE; - if (!hapd->conf->wpa && - (hapd->default_wep_key || hapd->conf->individual_wep_key_len > 0)) + if (!eapol->conf.wpa && + (hapd->default_wep_key || eapol->conf.individual_wep_key_len > 0)) sm->keyTxEnabled = TRUE; else sm->keyTxEnabled = FALSE; - if (hapd->conf->wpa) + if (eapol->conf.wpa) sm->portValid = FALSE; else sm->portValid = TRUE; - if (hapd->conf->eap_server) { - struct eap_config eap_conf; - memset(&eap_conf, 0, sizeof(eap_conf)); - eap_conf.ssl_ctx = hapd->ssl_ctx; - eap_conf.eap_sim_db_priv = hapd->eap_sim_db_priv; - sm->eap = eap_sm_init(sm, &eapol_cb, &eap_conf); - if (sm->eap == NULL) { - eapol_sm_free(sm); - return NULL; - } + os_memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.eap_server = eapol->conf.eap_server; + eap_conf.ssl_ctx = eapol->conf.ssl_ctx; + eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; + eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; + eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; + eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; + eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; + eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; + eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; + eap_conf.tnc = eapol->conf.tnc; + eap_conf.wps = eapol->conf.wps; + eap_conf.assoc_wps_ie = sta->wps_ie; + sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); + if (sm->eap == NULL) { + eapol_auth_free(sm); + return NULL; } + sm->eap_if = eap_get_interface(sm->eap); - eapol_sm_initialize(sm); + eapol_auth_initialize(sm); return sm; } -void eapol_sm_free(struct eapol_state_machine *sm) +void eapol_auth_free(struct eapol_state_machine *sm) { if (sm == NULL) return; - eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); if (sm->eap) - eap_sm_deinit(sm->eap); - free(sm); + eap_server_sm_deinit(sm->eap); + os_free(sm); } -static int eapol_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, + const u8 *addr) { - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->eapol_sm == NULL) - return 0; - return 1; + return eapol->cb.sta_entry_alive(eapol->conf.hapd, addr); } static void eapol_sm_step_run(struct eapol_state_machine *sm) { - struct hostapd_data *hapd = sm->hapd; + struct eapol_authenticator *eapol = sm->eapol; u8 addr[ETH_ALEN]; unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; int max_steps = 100; - memcpy(addr, sm->sta->addr, ETH_ALEN); + os_memcpy(addr, sm->addr, ETH_ALEN); /* * Allow EAPOL state machines to run as long as there are state @@ -792,15 +886,15 @@ restart: prev_ctrl_dir = sm->ctrl_dir_state; SM_STEP_RUN(AUTH_PAE); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(BE_AUTH); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(REAUTH_TIMER); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(AUTH_KEY_TX); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(KEY_RX); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) SM_STEP_RUN(CTRL_DIR); if (prev_auth_pae != sm->auth_pae_state || @@ -812,21 +906,35 @@ restart: if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ - eapol_sm_step(sm); + eapol_auth_step(sm); return; } - if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) { - if (eap_sm_step(sm->eap)) { + if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { + if (eap_server_sm_step(sm->eap)) { if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ - eapol_sm_step(sm); + eapol_auth_step(sm); return; } + + /* TODO: find a better location for this */ + if (sm->eap_if->aaaEapResp) { + sm->eap_if->aaaEapResp = FALSE; + if (sm->eap_if->aaaEapRespData == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " + "but no aaaEapRespData available"); + return; + } + sm->eapol->cb.aaa_send( + sm->hapd, sm->sta, + wpabuf_head(sm->eap_if->aaaEapRespData), + wpabuf_len(sm->eap_if->aaaEapRespData)); + } } - if (eapol_sm_sta_entry_alive(hapd, addr)) + if (eapol_sm_sta_entry_alive(eapol, addr)) wpa_auth_sm_notify(sm->sta->wpa_sm); } @@ -838,7 +946,14 @@ static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) } -void eapol_sm_step(struct eapol_state_machine *sm) +/** + * eapol_auth_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance EAPOL state machines after any change + * that could affect their state. + */ +void eapol_auth_step(struct eapol_state_machine *sm) { /* * Run eapol_sm_step_run from a registered timeout to make sure that @@ -850,7 +965,7 @@ void eapol_sm_step(struct eapol_state_machine *sm) } -void eapol_sm_initialize(struct eapol_state_machine *sm) +void eapol_auth_initialize(struct eapol_state_machine *sm) { sm->initializing = TRUE; /* Initialize the state machines by asserting initialize and then @@ -862,8 +977,8 @@ void eapol_sm_initialize(struct eapol_state_machine *sm) sm->initializing = FALSE; /* Start one second tick for port timers state machine */ - eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); - eloop_register_timeout(1, 0, eapol_port_timers_tick, sm->hapd, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); } @@ -973,8 +1088,8 @@ static inline const char * ctrl_dir_state_txt(int s) } -void eapol_sm_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm) +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm) { fprintf(f, "%sEAPOL state machine:\n", prefix); fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, @@ -990,12 +1105,13 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, prefix, _SB(sm->authAbort), _SB(sm->authFail), port_state_txt(sm->authPortStatus), _SB(sm->authStart), prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), - _SB(sm->eapFail), _SB(sm->eapolEap), - prefix, _SB(sm->eapSuccess), _SB(sm->eapTimeout), - _SB(sm->initialize), _SB(sm->keyAvailable), + _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), + prefix, _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), prefix, _SB(sm->keyDone), _SB(sm->keyRun), _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), - prefix, _SB(sm->portEnabled), _SB(sm->portValid), + prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), _SB(sm->reAuthenticate)); fprintf(f, "%s Authenticator PAE:\n" @@ -1015,7 +1131,8 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s authAuthEapStartsWhileAuthenticated=%d\n" "%s authAuthEapLogoffWhileAuthenticated=%d\n", prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, - _SB(sm->eapolLogoff), _SB(sm->eapolStart), _SB(sm->eapRestart), + _SB(sm->eapolLogoff), _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), prefix, port_type_txt(sm->portMode), sm->reAuthCount, prefix, sm->quietPeriod, sm->reAuthMax, prefix, sm->authEntersConnecting, @@ -1041,7 +1158,8 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s backendAuthFails=%d\n", prefix, prefix, be_auth_state_txt(sm->be_auth_state), - prefix, _SB(sm->eapNoReq), _SB(sm->eapReq), _SB(sm->eapResp), + prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), prefix, sm->serverTimeout, prefix, sm->backendResponses, prefix, sm->backendAccessChallenges, @@ -1078,187 +1196,147 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, #endif /* HOSTAPD_DUMP_STATE */ -static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) { struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return FALSE; - switch (variable) { - case EAPOL_eapSuccess: - return sm->eapSuccess; - case EAPOL_eapRestart: - return sm->eapRestart; - case EAPOL_eapFail: - return sm->eapFail; - case EAPOL_eapResp: - return sm->eapResp; - case EAPOL_eapReq: - return sm->eapReq; - case EAPOL_eapNoReq: - return sm->eapNoReq; - case EAPOL_portEnabled: - return sm->portEnabled; - case EAPOL_eapTimeout: - return sm->eapTimeout; - } - return FALSE; + return sm->eapol->cb.get_eap_user(sm->hapd, identity, identity_len, + phase2, user); } -static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, - Boolean value) +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) { struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return; - switch (variable) { - case EAPOL_eapSuccess: - sm->eapSuccess = value; - break; - case EAPOL_eapRestart: - sm->eapRestart = value; - break; - case EAPOL_eapFail: - sm->eapFail = value; - break; - case EAPOL_eapResp: - sm->eapResp = value; - break; - case EAPOL_eapReq: - sm->eapReq = value; - break; - case EAPOL_eapNoReq: - sm->eapNoReq = value; - break; - case EAPOL_portEnabled: - sm->portEnabled = value; - break; - case EAPOL_eapTimeout: - sm->eapTimeout = value; - break; - } + *len = sm->eapol->conf.eap_req_id_text_len; + return sm->eapol->conf.eap_req_id_text; } -static void eapol_sm_set_eapReqData(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen) +static struct eapol_callbacks eapol_cb = { - struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return; - - free(sm->last_eap_radius); - sm->last_eap_radius = malloc(eapReqDataLen); - if (sm->last_eap_radius == NULL) - return; - memcpy(sm->last_eap_radius, eapReqData, eapReqDataLen); - sm->last_eap_radius_len = eapReqDataLen; -} + .get_eap_user = eapol_sm_get_eap_user, + .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, +}; -static void eapol_sm_set_eapKeyData(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen) +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) { - struct eapol_state_machine *sm = ctx; - struct hostapd_data *hapd; - - if (sm == NULL) - return; + if (sm == NULL || ctx != sm->eap) + return -1; - hapd = sm->hapd; + eap_sm_pending_cb(sm->eap); + eapol_auth_step(sm); - if (eapKeyData && eapKeyDataLen >= 64) { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_crypt = malloc(32); - if (sm->eapol_key_crypt) { - memcpy(sm->eapol_key_crypt, eapKeyData, 32); - sm->eapol_key_crypt_len = 32; - } - sm->eapol_key_sign = malloc(32); - if (sm->eapol_key_sign) { - memcpy(sm->eapol_key_sign, eapKeyData + 32, 32); - sm->eapol_key_sign_len = 32; - } - if (hapd->default_wep_key || - hapd->conf->individual_wep_key_len > 0 || - hapd->conf->wpa) - sm->keyAvailable = TRUE; - } else { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_sign = NULL; - sm->eapol_key_crypt = NULL; - sm->eapol_key_sign_len = 0; - sm->eapol_key_crypt_len = 0; - sm->keyAvailable = FALSE; - } + return 0; } -static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, - size_t identity_len, int phase2, - struct eap_user *user) +static int eapol_auth_conf_clone(struct eapol_auth_config *dst, + struct eapol_auth_config *src) { - struct eapol_state_machine *sm = ctx; - const struct hostapd_eap_user *eap_user; - int i, count; - - eap_user = hostapd_get_eap_user(sm->hapd->conf, identity, - identity_len, phase2); - if (eap_user == NULL) - return -1; - - memset(user, 0, sizeof(*user)); - user->phase2 = phase2; - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { - user->methods[i].vendor = eap_user->methods[i].vendor; - user->methods[i].method = eap_user->methods[i].method; - } - - if (eap_user->password) { - user->password = malloc(eap_user->password_len); - if (user->password == NULL) + dst->hapd = src->hapd; + dst->eap_reauth_period = src->eap_reauth_period; + dst->wpa = src->wpa; + dst->individual_wep_key_len = src->individual_wep_key_len; + dst->eap_server = src->eap_server; + dst->ssl_ctx = src->ssl_ctx; + dst->eap_sim_db_priv = src->eap_sim_db_priv; + os_free(dst->eap_req_id_text); + if (src->eap_req_id_text) { + dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); + if (dst->eap_req_id_text == NULL) return -1; - memcpy(user->password, eap_user->password, - eap_user->password_len); - user->password_len = eap_user->password_len; + os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, + src->eap_req_id_text_len); + dst->eap_req_id_text_len = src->eap_req_id_text_len; + } else { + dst->eap_req_id_text = NULL; + dst->eap_req_id_text_len = 0; } - user->force_version = eap_user->force_version; - + if (src->pac_opaque_encr_key) { + dst->pac_opaque_encr_key = os_malloc(16); + os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, + 16); + } else + dst->pac_opaque_encr_key = NULL; + if (src->eap_fast_a_id) { + dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + if (dst->eap_fast_a_id == NULL) { + os_free(dst->eap_req_id_text); + return -1; + } + os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, + src->eap_fast_a_id_len); + dst->eap_fast_a_id_len = src->eap_fast_a_id_len; + } else + dst->eap_fast_a_id = NULL; + if (src->eap_fast_a_id_info) { + dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); + if (dst->eap_fast_a_id_info == NULL) { + os_free(dst->eap_req_id_text); + os_free(dst->eap_fast_a_id); + return -1; + } + } else + dst->eap_fast_a_id_info = NULL; + dst->eap_fast_prov = src->eap_fast_prov; + dst->pac_key_lifetime = src->pac_key_lifetime; + dst->pac_key_refresh_time = src->pac_key_refresh_time; + dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; + dst->tnc = src->tnc; + dst->wps = src->wps; return 0; } -static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +static void eapol_auth_conf_free(struct eapol_auth_config *conf) { - struct eapol_state_machine *sm = ctx; - *len = sm->hapd->conf->eap_req_id_text_len; - return sm->hapd->conf->eap_req_id_text; + os_free(conf->eap_req_id_text); + conf->eap_req_id_text = NULL; + os_free(conf->pac_opaque_encr_key); + conf->pac_opaque_encr_key = NULL; + os_free(conf->eap_fast_a_id); + conf->eap_fast_a_id = NULL; + os_free(conf->eap_fast_a_id_info); + conf->eap_fast_a_id_info = NULL; } -static struct eapol_callbacks eapol_cb = +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb) { - .get_bool = eapol_sm_get_bool, - .set_bool = eapol_sm_set_bool, - .set_eapReqData = eapol_sm_set_eapReqData, - .set_eapKeyData = eapol_sm_set_eapKeyData, - .get_eap_user = eapol_sm_get_eap_user, - .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, -}; + struct eapol_authenticator *eapol; + eapol = os_zalloc(sizeof(*eapol)); + if (eapol == NULL) + return NULL; -int eapol_sm_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) -{ - if (sm == NULL || ctx != sm->eap) - return -1; + if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { + os_free(eapol); + return NULL; + } - eap_sm_pending_cb(sm->eap); - eapol_sm_step(sm); + eapol->cb.eapol_send = cb->eapol_send; + eapol->cb.aaa_send = cb->aaa_send; + eapol->cb.finished = cb->finished; + eapol->cb.get_eap_user = cb->get_eap_user; + eapol->cb.sta_entry_alive = cb->sta_entry_alive; + eapol->cb.logger = cb->logger; + eapol->cb.set_port_authorized = cb->set_port_authorized; + eapol->cb.abort_auth = cb->abort_auth; + eapol->cb.tx_key = cb->tx_key; + + return eapol; +} - return 0; + +void eapol_auth_deinit(struct eapol_authenticator *eapol) +{ + if (eapol == NULL) + return; + + eapol_auth_conf_free(&eapol->conf); + os_free(eapol); } diff --git a/contrib/hostapd/eapol_sm.h b/hostapd/eapol_sm.h index dcb5ee9b31967..7a13e8e7c003b 100644 --- a/contrib/hostapd/eapol_sm.h +++ b/hostapd/eapol_sm.h @@ -1,6 +1,6 @@ /* - * hostapd / IEEE 802.1X Authenticator - EAPOL state machine - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -37,6 +37,69 @@ struct radius_class_data { size_t count; }; + +struct eapol_auth_config { + int eap_reauth_period; + int wpa; + int individual_wep_key_len; + int eap_server; + void *ssl_ctx; + void *eap_sim_db_priv; + char *eap_req_id_text; /* a copy of this will be allocated */ + size_t eap_req_id_text_len; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + + /* + * Pointer to hostapd data. This is a temporary workaround for + * transition phase and will be removed once IEEE 802.1X/EAPOL code is + * separated more cleanly from rest of hostapd. + */ + struct hostapd_data *hapd; +}; + +struct eap_user; + +typedef enum { + EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING +} eapol_logger_level; + +struct eapol_auth_cb { + void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, + size_t datalen); + void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, + size_t datalen); + void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + int (*sta_entry_alive)(void *ctx, const u8 *addr); + void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, + const char *txt); + void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); + void (*abort_auth)(void *ctx, void *sta_ctx); + void (*tx_key)(void *ctx, void *sta_ctx); +}; + +/** + * struct eapol_authenticator - Global EAPOL authenticator data + */ +struct eapol_authenticator { + struct eapol_auth_config conf; + struct eapol_auth_cb cb; +}; + + +/** + * struct eapol_state_machine - Per-Supplicant Authenticator state machines + */ struct eapol_state_machine { /* timers */ int aWhile; @@ -50,17 +113,12 @@ struct eapol_state_machine { Boolean authStart; Boolean authTimeout; Boolean authSuccess; - Boolean eapFail; Boolean eapolEap; - Boolean eapSuccess; - Boolean eapTimeout; Boolean initialize; - Boolean keyAvailable; Boolean keyDone; Boolean keyRun; Boolean keyTxEnabled; PortTypes portControl; - Boolean portEnabled; Boolean portValid; Boolean reAuthenticate; @@ -75,7 +133,6 @@ struct eapol_state_machine { /* variables */ Boolean eapolLogoff; Boolean eapolStart; - Boolean eapRestart; PortTypes portMode; unsigned int reAuthCount; /* constants */ @@ -101,10 +158,6 @@ struct eapol_state_machine { BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, BE_AUTH_IGNORE } be_auth_state; - /* variables */ - Boolean eapNoReq; - Boolean eapReq; - Boolean eapResp; /* constants */ unsigned int serverTimeout; /* default 30; 1..X */ #define BE_AUTH_DEFAULT_serverTimeout 30 @@ -154,16 +207,16 @@ struct eapol_state_machine { /* Other variables - not defined in IEEE 802.1X */ u8 addr[ETH_ALEN]; /* Supplicant address */ #define EAPOL_SM_PREAUTH BIT(0) +#define EAPOL_SM_WAIT_START BIT(1) int flags; /* EAPOL_SM_* */ + /* EAPOL/AAA <-> EAP full authenticator interface */ + struct eap_eapol_interface *eap_if; + int radius_identifier; /* TODO: check when the last messages can be released */ struct radius_msg *last_recv_radius; - u8 *last_eap_supp; /* last received EAP Response from Supplicant */ - size_t last_eap_supp_len; - u8 *last_eap_radius; /* last received EAP Response from Authentication - * Server */ - size_t last_eap_radius_len; + u8 last_eap_id; /* last used EAP Identifier */ u8 *identity; size_t identity_len; u8 eap_type_authsrv; /* EAP type of the last EAP packet from @@ -177,20 +230,13 @@ struct eapol_state_machine { u8 *eapol_key_crypt; size_t eapol_key_crypt_len; - Boolean rx_identity; /* set to TRUE on reception of - * EAP-Response/Identity */ - struct eap_sm *eap; - /* currentId was removed in IEEE 802.1X-REV, but it is needed to filter - * out EAP-Responses to old packets (e.g., to two EAP-Request/Identity - * packets that are often sent in the beginning of the authentication). - */ - u8 currentId; - Boolean initializing; /* in process of initializing state machines */ Boolean changed; + struct eapol_authenticator *eapol; + /* Somewhat nasty pointers to global hostapd and STA data to avoid * passing these to every function */ struct hostapd_data *hapd; @@ -198,13 +244,17 @@ struct eapol_state_machine { }; -struct eapol_state_machine *eapol_sm_alloc(struct hostapd_data *hapd, - struct sta_info *sta); -void eapol_sm_free(struct eapol_state_machine *sm); -void eapol_sm_step(struct eapol_state_machine *sm); -void eapol_sm_initialize(struct eapol_state_machine *sm); -void eapol_sm_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm); -int eapol_sm_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb); +void eapol_auth_deinit(struct eapol_authenticator *eapol); +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int preauth, struct sta_info *sta); +void eapol_auth_free(struct eapol_state_machine *sm); +void eapol_auth_step(struct eapol_state_machine *sm); +void eapol_auth_initialize(struct eapol_state_machine *sm); +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); #endif /* EAPOL_SM_H */ diff --git a/contrib/hostapd/hostap_common.h b/hostapd/hostap_common.h index 1e38df38fafb4..5a57dca46d3e7 100644 --- a/contrib/hostapd/hostap_common.h +++ b/hostapd/hostap_common.h @@ -147,9 +147,9 @@ enum { #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_RID_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +((size_t) (&((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)) +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() */ diff --git a/contrib/hostapd/hostapd.8 b/hostapd/hostapd.8 index 9258512fd55e4..9258512fd55e4 100644 --- a/contrib/hostapd/hostapd.8 +++ b/hostapd/hostapd.8 diff --git a/hostapd/hostapd.accept b/hostapd/hostapd.accept new file mode 100644 index 0000000000000..2d2a0a27efdc8 --- /dev/null +++ b/hostapd/hostapd.accept @@ -0,0 +1,6 @@ +# List of MAC addresses that are allowed to authenticate (IEEE 802.11) +# with the AP. Optional VLAN ID can be assigned for clients based on the +# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used. +00:11:22:33:44:55 +00:66:77:88:99:aa +00:00:22:33:44:55 1 diff --git a/contrib/hostapd/hostapd.c b/hostapd/hostapd.c index 87a552379646f..df6062bf2f05d 100644 --- a/contrib/hostapd/hostapd.c +++ b/hostapd/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -31,19 +31,29 @@ #include "ap_list.h" #include "sta_info.h" #include "driver.h" -#include "radius_client.h" -#include "radius_server.h" +#include "radius/radius_client.h" +#include "radius/radius_server.h" #include "wpa.h" #include "preauth.h" #include "wme.h" #include "vlan_init.h" #include "ctrl_iface.h" #include "tls.h" -#include "eap_sim_db.h" -#include "eap.h" +#include "eap_server/eap_sim_db.h" +#include "eap_server/eap.h" +#include "eap_server/tncs.h" #include "version.h" +#include "l2_packet/l2_packet.h" +#include "wps_hostapd.h" +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user); +static int hostapd_flush_old_stations(struct hostapd_data *hapd); +static int hostapd_setup_wpa(struct hostapd_data *hapd); +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); + struct hapd_interfaces { size_t count; struct hostapd_iface **iface; @@ -57,17 +67,17 @@ extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; -void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, - unsigned int module, int level, const char *fmt, ...) +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) { + struct hostapd_data *hapd = ctx; char *format, *module_str; int maxlen; - va_list ap; int conf_syslog_level, conf_stdout_level; unsigned int conf_syslog, conf_stdout; - maxlen = strlen(fmt) + 100; - format = malloc(maxlen); + maxlen = len + 100; + format = os_malloc(maxlen); if (!format) return; @@ -109,27 +119,24 @@ void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, } if (hapd && hapd->conf && addr) - snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", - hapd->conf->iface, MAC2STR(addr), - module_str ? " " : "", module_str, fmt); + os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", + hapd->conf->iface, MAC2STR(addr), + module_str ? " " : "", module_str, txt); else if (hapd && hapd->conf) - snprintf(format, maxlen, "%s:%s%s %s", - hapd->conf->iface, module_str ? " " : "", - module_str, fmt); + os_snprintf(format, maxlen, "%s:%s%s %s", + hapd->conf->iface, module_str ? " " : "", + module_str, txt); else if (addr) - snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", - MAC2STR(addr), module_str ? " " : "", - module_str, fmt); + os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", + MAC2STR(addr), module_str ? " " : "", + module_str, txt); else - snprintf(format, maxlen, "%s%s%s", - module_str, module_str ? ": " : "", fmt); + os_snprintf(format, maxlen, "%s%s%s", + module_str, module_str ? ": " : "", txt); if ((conf_stdout & module) && level >= conf_stdout_level) { wpa_debug_print_timestamp(); - va_start(ap, fmt); - vprintf(format, ap); - va_end(ap); - printf("\n"); + printf("%s\n", format); } #ifndef CONFIG_NATIVE_WINDOWS @@ -153,76 +160,28 @@ void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, priority = LOG_INFO; break; } - va_start(ap, fmt); - vsyslog(priority, format, ap); - va_end(ap); + syslog(priority, "%s", format); } #endif /* CONFIG_NATIVE_WINDOWS */ - free(format); -} - - -const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, - size_t buflen) -{ - if (buflen == 0 || addr == NULL) - return NULL; - - if (addr->af == AF_INET) { - 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; -} - - -int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b) -{ - if (a == NULL && b == NULL) - return 0; - if (a == NULL || b == NULL) - return 1; - - switch (a->af) { - case AF_INET: - if (a->u.v4.s_addr != b->u.v4.s_addr) - return 1; - break; -#ifdef CONFIG_IPV6 - case AF_INET6: - if (memcpy(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) - != 0) - return 1; - break; -#endif /* CONFIG_IPV6 */ - } - - return 0; + os_free(format); } static void hostapd_deauth_all_stas(struct hostapd_data *hapd) { -#if 0 u8 addr[ETH_ALEN]; - memset(addr, 0xff, ETH_ALEN); - hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); -#else /* New Prism2.5/3 STA firmware versions seem to have issues with this * broadcast deauth frame. This gets the firmware in odd state where - * nothing works correctly, so let's skip sending this for a while - * until the issue has been resolved. */ -#endif + * nothing works correctly, so let's skip sending this for the hostap + * driver. */ + + if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { + os_memset(addr, 0xff, ETH_ALEN); + hostapd_sta_deauth(hapd, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } } @@ -294,9 +253,11 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); - if (reassoc) - wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); - else + if (reassoc) { + if (sta->auth_alg != WLAN_AUTH_FT && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); + } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); } @@ -305,7 +266,7 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { - if (eapol_sm_eap_pending_cb(sta->eapol_sm, ctx) == 0) + if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0) return 1; return 0; } @@ -320,9 +281,12 @@ static void hostapd_sim_db_cb(void *ctx, void *session_ctx) #endif /* EAP_SERVER */ +/** + * handle_term - SIGINT and SIGTERM handler to terminate hostapd process + */ static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) { - printf("Signal %d received - terminating\n", sig); + wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); eloop_terminate(); } @@ -337,56 +301,118 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wpa_group_rekey = conf->wpa_group_rekey; wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; + wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; wconf->peerkey = conf->peerkey; wconf->wme_enabled = conf->wme_enabled; + wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + wconf->ssid_len = conf->ssid.ssid_len; + if (wconf->ssid_len > SSID_LEN) + wconf->ssid_len = SSID_LEN; + os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); + os_memcpy(wconf->mobility_domain, conf->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + if (conf->nas_identifier && + os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { + wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); + os_memcpy(wconf->r0_key_holder, conf->nas_identifier, + wconf->r0_key_holder_len); + } + os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); + wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->reassociation_deadline = conf->reassociation_deadline; + wconf->r0kh_list = conf->r0kh_list; + wconf->r1kh_list = conf->r1kh_list; + wconf->pmk_r1_push = conf->pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ } -#ifndef CONFIG_NATIVE_WINDOWS -static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) +int hostapd_reload_config(struct hostapd_iface *iface) { - struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - struct hostapd_config *newconf; - size_t i; + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; struct wpa_auth_config wpa_auth_conf; - printf("Signal %d received - reloading configuration\n", sig); + newconf = hostapd_config_read(iface->config_fname); + if (newconf == NULL) + return -1; - for (i = 0; i < hapds->count; i++) { - struct hostapd_data *hapd = hapds->iface[i]->bss[0]; - newconf = hostapd_config_read(hapds->iface[i]->config_fname); - if (newconf == NULL) { - printf("Failed to read new configuration file - " - "continuing with old.\n"); - continue; - } - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, remove stations added to - * deny list, etc.) */ - radius_client_flush(hapd->radius, 0); - hostapd_config_free(hapd->iconf); + /* + * Deauthenticate all stations since the new configuration may not + * allow them to use the BSS anymore. + */ + hostapd_flush_old_stations(hapd); + + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(hapd->radius, 0); + + oldconf = hapd->iconf; + hapd->iconf = newconf; + hapd->conf = &newconf->bss[0]; + iface->conf = newconf; + if (hostapd_setup_wpa_psk(hapd->conf)) { + wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " + "after reloading configuration"); + } + + if (hapd->conf->wpa && hapd->wpa_auth == NULL) + hostapd_setup_wpa(hapd); + else if (hapd->conf->wpa) { hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); + } else if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); + } - hapd->iconf = newconf; - hapd->conf = &newconf->bss[0]; - hapds->iface[i]->conf = newconf; + ieee802_11_set_beacon(hapd); - if (hostapd_setup_wpa_psk(hapd->conf)) { - wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " - "after reloading configuration"); + hostapd_config_free(oldconf); + + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); + + return 0; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +/** + * handle_reload - SIGHUP handler to reload configuration + */ +static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) +{ + struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; + size_t i; + + wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", + sig); + + for (i = 0; i < hapds->count; i++) { + if (hostapd_reload_config(hapds->iface[i]) < 0) { + wpa_printf(MSG_WARNING, "Failed to read new " + "configuration file - continuing with " + "old."); + continue; } } } #ifdef HOSTAPD_DUMP_STATE +/** + * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file + */ static void hostapd_dump_state(struct hostapd_data *hapd) { FILE *f; @@ -396,15 +422,17 @@ static void hostapd_dump_state(struct hostapd_data *hapd) char *buf; if (!hapd->conf->dump_log_name) { - printf("Dump file not defined - ignoring dump request\n"); + wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump " + "request"); return; } - printf("Dumping hostapd state to '%s'\n", hapd->conf->dump_log_name); + wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'", + hapd->conf->dump_log_name); f = fopen(hapd->conf->dump_log_name, "w"); if (f == NULL) { - printf("Could not open dump file '%s' for writing.\n", - hapd->conf->dump_log_name); + wpa_printf(MSG_WARNING, "Could not open dump file '%s' for " + "writing.", hapd->conf->dump_log_name); return; } @@ -421,7 +449,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd) fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); fprintf(f, - " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s\n" + " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" " capability=0x%x listen_interval=%d\n", sta->aid, sta->flags, @@ -437,6 +465,10 @@ static void hostapd_dump_state(struct hostapd_data *hapd) (sta->flags & WLAN_STA_SHORT_PREAMBLE ? "[SHORT_PREAMBLE]" : ""), (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (sta->flags & WLAN_STA_WME ? "[WME]" : ""), + (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""), + (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), + (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), sta->capability, sta->listen_interval); @@ -455,7 +487,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd) ieee802_1x_dump_state(f, " ", sta); } - buf = malloc(4096); + buf = os_malloc(4096); if (buf) { int count = radius_client_get_mib(hapd->radius, buf, 4096); if (count < 0) @@ -472,7 +504,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd) count = 4095; buf[count] = '\0'; fprintf(f, "%s", buf); - free(buf); + os_free(buf); } fclose(f); } @@ -502,10 +534,24 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_set_encryption(ifname, hapd, "none", NULL, i, NULL, 0, i == 0 ? 1 : 0)) { - printf("Failed to clear default encryption keys " - "(ifname=%s keyidx=%d)\n", ifname, i); + wpa_printf(MSG_DEBUG, "Failed to clear default " + "encryption keys (ifname=%s keyidx=%d)", + ifname, i); } } +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w) { + for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { + if (hostapd_set_encryption(ifname, hapd, "none", NULL, + i, NULL, 0, + i == 0 ? 1 : 0)) { + wpa_printf(MSG_DEBUG, "Failed to clear " + "default mgmt encryption keys " + "(ifname=%s keyidx=%d)", ifname, i); + } + } + } +#endif /* CONFIG_IEEE80211W */ } @@ -528,7 +574,7 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) ssid->wep.key[idx], ssid->wep.len[idx], idx == ssid->wep.idx)) { - printf("Could not set WEP encryption.\n"); + wpa_printf(MSG_WARNING, "Could not set WEP encryption."); errors++; } @@ -549,8 +595,8 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) idx, key->key[idx], key->len[idx], idx == key->idx)) { - printf("Could not set dynamic VLAN WEP " - "encryption.\n"); + wpa_printf(MSG_WARNING, "Could not set " + "dynamic VLAN WEP encryption."); errors++; } } @@ -573,7 +619,7 @@ static void hostapd_cleanup(struct hostapd_data *hapd) { hostapd_ctrl_iface_deinit(hapd); - free(hapd->default_wep_key); + os_free(hapd->default_wep_key); hapd->default_wep_key = NULL; iapp_deinit(hapd->iapp); hapd->iapp = NULL; @@ -603,6 +649,12 @@ static void hostapd_cleanup(struct hostapd_data *hapd) radius_server_deinit(hapd->radius_srv); hapd->radius_srv = NULL; +#ifdef CONFIG_IEEE80211R + l2_packet_deinit(hapd->l2); +#endif /* CONFIG_IEEE80211R */ + + hostapd_deinit_wps(hapd); + hostapd_wireless_event_deinit(hapd); #ifdef EAP_TLS_FUNCS @@ -621,8 +673,8 @@ static void hostapd_cleanup(struct hostapd_data *hapd) if (hapd->interface_added && hostapd_bss_remove(hapd, hapd->conf->iface)) { - printf("Failed to remove BSS interface %s\n", - hapd->conf->iface); + wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", + hapd->conf->iface); } } @@ -650,15 +702,15 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; - free(iface->current_rates); + os_free(iface->current_rates); iface->current_rates = NULL; ap_list_deinit(iface); hostapd_config_free(iface->conf); iface->conf = NULL; - free(iface->config_fname); - free(iface->bss); - free(iface); + os_free(iface->config_fname); + os_free(iface->bss); + os_free(iface); } @@ -677,7 +729,8 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) i, hapd->conf->ssid.wep.key[i], hapd->conf->ssid.wep.len[i], i == hapd->conf->ssid.wep.idx)) { - printf("Could not set WEP encryption.\n"); + wpa_printf(MSG_WARNING, "Could not set WEP " + "encryption."); return -1; } if (hapd->conf->ssid.wep.key[i] && @@ -693,9 +746,12 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd) { int ret = 0; + if (hostapd_drv_none(hapd)) + return 0; + wpa_printf(MSG_DEBUG, "Flushing old station entries"); if (hostapd_flush(hapd)) { - printf("Could not connect to kernel driver.\n"); + wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); ret = -1; } wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); @@ -783,7 +839,8 @@ static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, break; case WPA_EAPOL_keyAvailable: if (sta->eapol_sm) - sta->eapol_sm->keyAvailable = value ? TRUE : FALSE; + sta->eapol_sm->eap_if->eapKeyAvailable = + value ? TRUE : FALSE; break; case WPA_EAPOL_keyDone: if (sta->eapol_sm) @@ -808,7 +865,7 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, case WPA_EAPOL_keyRun: return sta->eapol_sm->keyRun; case WPA_EAPOL_keyAvailable: - return sta->eapol_sm->keyAvailable; + return sta->eapol_sm->eap_if->eapKeyAvailable; default: return -1; } @@ -823,11 +880,11 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, } -static int hostapd_wpa_auth_get_pmk(void *ctx, const u8 *addr, u8 *pmk, +static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, size_t *len) { struct hostapd_data *hapd = ctx; - u8 *key; + const u8 *key; size_t keylen; struct sta_info *sta; @@ -835,14 +892,15 @@ static int hostapd_wpa_auth_get_pmk(void *ctx, const u8 *addr, u8 *pmk, if (sta == NULL) return -1; - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &keylen); + key = ieee802_1x_get_key(sta->eapol_sm, &keylen); if (key == NULL) return -1; if (keylen > *len) - keylen = WPA_PMK_LEN; - memcpy(pmk, key, keylen); + keylen = *len; + os_memcpy(msk, key, keylen); *len = keylen; + return 0; } @@ -906,6 +964,106 @@ static int hostapd_wpa_auth_for_each_sta( } +static int hostapd_wpa_auth_for_each_auth( + void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *ohapd; + size_t i, j; + struct hapd_interfaces *interfaces = eloop_get_user_data(); + + for (i = 0; i < interfaces->count; i++) { + for (j = 0; j < interfaces->iface[i]->num_bss; j++) { + ohapd = interfaces->iface[i]->bss[j]; + if (cb(ohapd->wpa_auth, cb_ctx)) + return 1; + } + } + + return 0; +} + + +static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->driver && hapd->driver->send_ether) + return hapd->driver->send_ether(hapd->drv_priv, dst, + hapd->own_addr, proto, + data, data_len); + if (hapd->l2 == NULL) + return -1; + return l2_packet_send(hapd->l2, dst, proto, data, data_len); +} + + +#ifdef CONFIG_IEEE80211R + +static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + int res; + struct ieee80211_mgmt *m; + size_t mlen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL || sta->wpa_sm == NULL) + return -1; + + m = os_zalloc(sizeof(*m) + data_len); + if (m == NULL) + return -1; + mlen = ((u8 *) &m->u - (u8 *) m) + data_len; + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, dst, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(&m->u, data, data_len); + + res = hostapd_send_mgmt_frame(hapd, (u8 *) m, mlen, 0); + os_free(m); + return res; +} + + +static struct wpa_state_machine * +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_sta_add(hapd, sta_addr); + if (sta == NULL) + return NULL; + if (sta->wpa_sm) + return sta->wpa_sm; + + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + ap_free_sta(hapd, sta); + return NULL; + } + sta->auth_alg = WLAN_AUTH_FT; + + return sta->wpa_sm; +} + + +static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); +} + +#endif /* CONFIG_IEEE80211R */ + + /** * hostapd_validate_bssid_configuration - Validate BSSID configuration * @iface: Pointer to interface data @@ -920,6 +1078,9 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) unsigned int i = iface->conf->num_bss, bits = 0, j; int res; + if (hostapd_drv_none(hapd)) + return 0; + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ /* Determine the bits necessary to cover the number of BSSIDs. */ @@ -957,7 +1118,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) if (bits > 40) return -1; - memset(mask, 0xff, ETH_ALEN); + os_memset(mask, 0xff, ETH_ALEN); j = bits / 8; for (i = 5; i > 5 - j; i--) mask[i] = 0; @@ -965,29 +1126,28 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) while (j--) mask[i] <<= 1; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "BSS count %lu, BSSID mask " - MACSTR " (%d bits)\n", - (unsigned long) iface->conf->num_bss, MAC2STR(mask), - bits); + wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", + (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); if (res == 0) return 0; if (res < 0) { - printf("Driver did not accept BSSID mask " MACSTR " for start " - "address " MACSTR ".\n", - MAC2STR(mask), MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " + MACSTR " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); return -1; } for (i = 0; i < ETH_ALEN; i++) { if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) { - printf("Invalid BSSID mask " MACSTR " for start " - "address " MACSTR ".\n" - "Start address must be the first address in the" - " block (i.e., addr AND mask == addr).\n", - MAC2STR(mask), MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR + " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Start address must be the " + "first address in the block (i.e., addr " + "AND mask == addr)."); return -1; } } @@ -1010,6 +1170,99 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a) } +static int hostapd_setup_wpa(struct hostapd_data *hapd) +{ + struct wpa_auth_config _conf; + struct wpa_auth_callbacks cb; + const u8 *wpa_ie; + size_t wpa_ie_len; + + hostapd_wpa_auth_conf(hapd->conf, &_conf); + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = hapd; + cb.logger = hostapd_wpa_auth_logger; + cb.disconnect = hostapd_wpa_auth_disconnect; + cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; + cb.set_eapol = hostapd_wpa_auth_set_eapol; + cb.get_eapol = hostapd_wpa_auth_get_eapol; + cb.get_psk = hostapd_wpa_auth_get_psk; + cb.get_msk = hostapd_wpa_auth_get_msk; + cb.set_key = hostapd_wpa_auth_set_key; + cb.get_seqnum = hostapd_wpa_auth_get_seqnum; + cb.get_seqnum_igtk = hostapd_wpa_auth_get_seqnum_igtk; + cb.send_eapol = hostapd_wpa_auth_send_eapol; + cb.for_each_sta = hostapd_wpa_auth_for_each_sta; + cb.for_each_auth = hostapd_wpa_auth_for_each_auth; + cb.send_ether = hostapd_wpa_auth_send_ether; +#ifdef CONFIG_IEEE80211R + cb.send_ft_action = hostapd_wpa_auth_send_ft_action; + cb.add_sta = hostapd_wpa_auth_add_sta; +#endif /* CONFIG_IEEE80211R */ + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + if (hapd->wpa_auth == NULL) { + wpa_printf(MSG_ERROR, "WPA initialization failed."); + return -1; + } + + if (hostapd_set_privacy(hapd, 1)) { + wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " + "for interface %s", hapd->conf->iface); + return -1; + } + + wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); + if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { + wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " + "the kernel driver."); + return -1; + } + + if (rsn_preauth_iface_init(hapd)) { + wpa_printf(MSG_ERROR, "Initialization of RSN " + "pre-authentication failed."); + return -1; + } + + return 0; + +} + + +static int hostapd_setup_radius_srv(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct radius_server_conf srv; + os_memset(&srv, 0, sizeof(srv)); + srv.client_file = conf->radius_server_clients; + srv.auth_port = conf->radius_server_auth_port; + srv.conf_ctx = conf; + srv.eap_sim_db_priv = hapd->eap_sim_db_priv; + srv.ssl_ctx = hapd->ssl_ctx; + srv.pac_opaque_encr_key = conf->pac_opaque_encr_key; + srv.eap_fast_a_id = conf->eap_fast_a_id; + srv.eap_fast_a_id_len = conf->eap_fast_a_id_len; + srv.eap_fast_a_id_info = conf->eap_fast_a_id_info; + srv.eap_fast_prov = conf->eap_fast_prov; + srv.pac_key_lifetime = conf->pac_key_lifetime; + srv.pac_key_refresh_time = conf->pac_key_refresh_time; + srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + srv.tnc = conf->tnc; + srv.wps = hapd->wps; + srv.ipv6 = conf->radius_server_ipv6; + srv.get_eap_user = hostapd_radius_get_eap_user; + srv.eap_req_id_text = conf->eap_req_id_text; + srv.eap_req_id_text_len = conf->eap_req_id_text_len; + + hapd->radius_srv = radius_server_init(&srv); + if (hapd->radius_srv == NULL) { + wpa_printf(MSG_ERROR, "RADIUS server initialization failed."); + return -1; + } + + return 0; +} + + /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data @@ -1034,14 +1287,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } else { /* Allocate the configured BSSID. */ - memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); + os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) == 0) { - printf("BSS '%s' may not have BSSID " - "set to the MAC address of the radio\n", - hapd->conf->iface); + wpa_printf(MSG_ERROR, "BSS '%s' may not have " + "BSSID set to the MAC address of " + "the radio", hapd->conf->iface); return -1; } } @@ -1049,8 +1302,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->interface_added = 1; if (hostapd_bss_add(hapd->iface->bss[0], hapd->conf->iface, hapd->own_addr)) { - printf("Failed to add BSS (BSSID=" MACSTR ")\n", - MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" + MACSTR ")", MAC2STR(hapd->own_addr)); return -1; } } @@ -1062,7 +1315,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) */ ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid)); if (ssid_len < 0) { - printf("Could not read SSID from system\n"); + wpa_printf(MSG_ERROR, "Could not read SSID from system"); return -1; } if (conf->ssid.ssid_set) { @@ -1072,7 +1325,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) * new SSID. */ set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len || - memcmp(conf->ssid.ssid, ssid, ssid_len) != 0); + os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0); } else { /* * No SSID in the config file; just use the one we got @@ -1080,29 +1333,33 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) */ set_ssid = 0; conf->ssid.ssid_len = ssid_len; - memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); + os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; } - printf("Using interface %s with hwaddr " MACSTR " and ssid '%s'\n", - hapd->conf->iface, MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + if (!hostapd_drv_none(hapd)) { + wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR + " and ssid '%s'", + hapd->conf->iface, MAC2STR(hapd->own_addr), + hapd->conf->ssid.ssid); + } if (hostapd_setup_wpa_psk(conf)) { - printf("WPA-PSK setup failed.\n"); + wpa_printf(MSG_ERROR, "WPA-PSK setup failed."); return -1; } /* Set flag for whether SSID is broadcast in beacons */ if (hostapd_set_broadcast_ssid(hapd, !!hapd->conf->ignore_broadcast_ssid)) { - printf("Could not set broadcast SSID flag for kernel " - "driver\n"); + wpa_printf(MSG_ERROR, "Could not set broadcast SSID flag for " + "kernel driver"); return -1; } if (hostapd_set_dtim_period(hapd, hapd->conf->dtim_period)) { - printf("Could not set DTIM period for kernel driver\n"); + wpa_printf(MSG_ERROR, "Could not set DTIM period for kernel " + "driver"); return -1; } @@ -1110,306 +1367,203 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) * response frames) */ if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, conf->ssid.ssid_len)) { - printf("Could not set SSID for kernel driver\n"); + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); return -1; } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + if (wpa_debug_level == MSG_MSGDUMP) conf->radius->msg_dumps = 1; hapd->radius = radius_client_init(hapd, conf->radius); if (hapd->radius == NULL) { - printf("RADIUS client initialization failed.\n"); + wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); return -1; } if (hostapd_acl_init(hapd)) { - printf("ACL initialization failed.\n"); + wpa_printf(MSG_ERROR, "ACL initialization failed."); return -1; } + if (hostapd_init_wps(hapd, conf)) + return -1; + if (ieee802_1x_init(hapd)) { - printf("IEEE 802.1X initialization failed.\n"); + wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); return -1; } - if (hapd->conf->wpa) { - struct wpa_auth_config _conf; - struct wpa_auth_callbacks cb; - const u8 *wpa_ie; - size_t wpa_ie_len; - - hostapd_wpa_auth_conf(hapd->conf, &_conf); - memset(&cb, 0, sizeof(cb)); - cb.ctx = hapd; - cb.logger = hostapd_wpa_auth_logger; - cb.disconnect = hostapd_wpa_auth_disconnect; - cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; - cb.set_eapol = hostapd_wpa_auth_set_eapol; - cb.get_eapol = hostapd_wpa_auth_get_eapol; - cb.get_psk = hostapd_wpa_auth_get_psk; - cb.get_pmk = hostapd_wpa_auth_get_pmk; - cb.set_key = hostapd_wpa_auth_set_key; - cb.get_seqnum = hostapd_wpa_auth_get_seqnum; - cb.get_seqnum_igtk = hostapd_wpa_auth_get_seqnum_igtk; - cb.send_eapol = hostapd_wpa_auth_send_eapol; - cb.for_each_sta = hostapd_wpa_auth_for_each_sta; - hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); - if (hapd->wpa_auth == NULL) { - printf("WPA initialization failed.\n"); - return -1; - } - - if (hostapd_set_privacy(hapd, 1)) { - wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " - "for interface %s", hapd->conf->iface); - return -1; - } - - wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); - if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { - wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " - "the kernel driver."); - return -1; - } - - if (rsn_preauth_iface_init(hapd)) { - printf("Initialization of RSN pre-authentication " - "failed.\n"); - return -1; - } - } + if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) + return -1; if (accounting_init(hapd)) { - printf("Accounting initialization failed.\n"); + wpa_printf(MSG_ERROR, "Accounting initialization failed."); return -1; } if (hapd->conf->ieee802_11f && (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { - printf("IEEE 802.11F (IAPP) initialization failed.\n"); + wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " + "failed."); return -1; } if (hostapd_ctrl_iface_init(hapd)) { - printf("Failed to setup control interface\n"); + wpa_printf(MSG_ERROR, "Failed to setup control interface"); return -1; } - ieee802_11_set_beacon(hapd); - - if (vlan_init(hapd)) { - printf("VLAN initialization failed.\n"); + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { + wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; } - return 0; -} - - -/** - * setup_interface2 - Setup (initialize) an interface (part 2) - * @iface: Pointer to interface data. - * Returns: 0 on success; -1 on failure. - * - * Flushes old stations, sets the channel, DFS parameters, encryption, - * beacons, and WDS links based on the configuration. - */ -static int setup_interface2(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - int freq; - size_t j; - int ret = 0; - u8 *prev_addr; - - hostapd_flush_old_stations(hapd); - hostapd_set_privacy(hapd, 0); - - if (hapd->iconf->channel) { - freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); - printf("Mode: %s Channel: %d Frequency: %d MHz\n", - hostapd_hw_mode_txt(hapd->iconf->hw_mode), - hapd->iconf->channel, freq); - - if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq)) { - printf("Could not set channel for kernel driver\n"); +#ifdef CONFIG_IEEE80211R + if (!hostapd_drv_none(hapd)) { + hapd->l2 = l2_packet_init(hapd->conf->iface, NULL, ETH_P_RRB, + hostapd_rrb_receive, hapd, 0); + if (hapd->l2 == NULL && + (hapd->driver == NULL || + hapd->driver->send_ether == NULL)) { + wpa_printf(MSG_ERROR, "Failed to open l2_packet " + "interface"); return -1; } } +#endif /* CONFIG_IEEE80211R */ - hostapd_broadcast_wep_clear(hapd); - if (hostapd_setup_encryption(hapd->conf->iface, hapd)) - return -1; - - hostapd_set_beacon_int(hapd, hapd->iconf->beacon_int); ieee802_11_set_beacon(hapd); - if (hapd->iconf->rts_threshold > -1 && - hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { - printf("Could not set RTS threshold for kernel driver\n"); + if (conf->radius_server_clients && + hostapd_setup_radius_srv(hapd, conf)) return -1; - } - if (hapd->iconf->fragm_threshold > -1 && - hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { - printf("Could not set fragmentation threshold for kernel " - "driver\n"); - return -1; - } + return 0; +} - prev_addr = hapd->own_addr; - for (j = 0; j < iface->num_bss; j++) { - hapd = iface->bss[j]; - if (j) - memcpy(hapd->own_addr, prev_addr, ETH_ALEN); - if (hostapd_setup_bss(hapd, j == 0)) - return -1; - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) - prev_addr = hapd->own_addr; - } +static void hostapd_tx_queue_params(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int i; + struct hostapd_tx_queue_params *p; - ap_list_init(iface); + for (i = 0; i < NUM_TX_QUEUES; i++) { + p = &iface->conf->tx_queue[i]; - if (hostapd_driver_commit(hapd) < 0) { - wpa_printf(MSG_ERROR, "%s: Failed to commit driver " - "configuration", __func__); - return -1; - } + if (!p->configured) + continue; - return ret; + if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, + p->cwmax, p->burst)) { + wpa_printf(MSG_DEBUG, "Failed to set TX queue " + "parameters for queue %d.", i); + /* Continue anyway */ + } + } } -static void setup_interface_start(void *eloop_data, void *user_ctx); -static void setup_interface2_handler(void *eloop_data, void *user_ctx); - -/** - * setup_interface_finalize - Finish setup interface & call the callback - * @iface: Pointer to interface data. - * @status: Status of the setup interface (0 on success; -1 on failure). - * Returns: 0 on success; -1 on failure (e.g., was not in progress). - */ -static int setup_interface_finalize(struct hostapd_iface *iface, int status) +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) { - hostapd_iface_cb cb; + const struct hostapd_eap_user *eap_user; + int i, count; - if (!iface->setup_cb) + eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); + if (eap_user == NULL) return -1; - - eloop_cancel_timeout(setup_interface_start, iface, NULL); - eloop_cancel_timeout(setup_interface2_handler, iface, NULL); - hostapd_select_hw_mode_stop(iface); - cb = iface->setup_cb; + if (user == NULL) + return 0; - iface->setup_cb = NULL; + os_memset(user, 0, sizeof(*user)); + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } - cb(iface, status); + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; return 0; } -/** - * setup_interface2_wrapper - Wrapper for setup_interface2() - * @iface: Pointer to interface data. - * @status: Status of the hw mode select. - * - * Wrapper for setup_interface2() to calls finalize function upon completion. - */ -static void setup_interface2_wrapper(struct hostapd_iface *iface, int status) -{ - int ret = status; - if (ret) - printf("Could not select hw_mode and channel. (%d)\n", ret); - else - ret = setup_interface2(iface); - - setup_interface_finalize(iface, ret); -} - - -/** - * setup_interface2_handler - Used for immediate call of setup_interface2 - * @eloop_data: Stores the struct hostapd_iface * for the interface. - * @user_ctx: Unused. - */ -static void setup_interface2_handler(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *iface = eloop_data; - - setup_interface2_wrapper(iface, 0); -} - - -/** - * setup_interface1 - Setup (initialize) an interface (part 1) - * @iface: Pointer to interface data - * Returns: 0 on success, -1 on failure - * - * Initializes the driver interface, validates the configuration, - * and sets driver parameters based on the configuration. - * Schedules setup_interface2() to be called immediately or after - * hardware mode setup takes place. - */ -static int setup_interface1(struct hostapd_iface *iface) +static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; struct hostapd_bss_config *conf = hapd->conf; size_t i; char country[4]; + u8 *b = conf->bssid; + int freq; + size_t j; + int ret = 0; + u8 *prev_addr; /* * Initialize the driver interface and make sure that all BSSes get * configured with a pointer to this driver interface. */ - if (hostapd_driver_init(hapd)) { - printf("%s driver initialization failed.\n", - hapd->driver ? hapd->driver->name : "Unknown"); + if (b[0] | b[1] | b[2] | b[3] | b[4] | b[5]) { + hapd->drv_priv = hostapd_driver_init_bssid(hapd, b); + } else { + hapd->drv_priv = hostapd_driver_init(hapd); + } + + if (hapd->drv_priv == NULL) { + wpa_printf(MSG_ERROR, "%s driver initialization failed.", + hapd->driver ? hapd->driver->name : "Unknown"); hapd->driver = NULL; return -1; } - for (i = 0; i < iface->num_bss; i++) + for (i = 0; i < iface->num_bss; i++) { iface->bss[i]->driver = hapd->driver; + iface->bss[i]->drv_priv = hapd->drv_priv; + } if (hostapd_validate_bssid_configuration(iface)) return -1; - memcpy(country, hapd->iconf->country, 3); - country[3] = '\0'; - if (hostapd_set_country(hapd, country) < 0) { - printf("Failed to set country code\n"); - return -1; - } - - if (hapd->iconf->ieee80211d || hapd->iconf->ieee80211h) { - if (hostapd_set_ieee80211d(hapd, 1) < 0) { - printf("Failed to set ieee80211d (%d)\n", - hapd->iconf->ieee80211d); +#ifdef CONFIG_IEEE80211N + SET_2BIT_LE16(&iface->ht_op_mode, + HT_INFO_OPERATION_MODE_OP_MODE_OFFSET, + OP_MODE_PURE); +#endif /* CONFIG_IEEE80211N */ + + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + os_memcpy(country, hapd->iconf->country, 3); + country[3] = '\0'; + if (hostapd_set_country(hapd, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } } - if (hapd->iconf->bridge_packets != INTERNAL_BRIDGE_DO_NOT_CONTROL && - hostapd_set_internal_bridge(hapd, hapd->iconf->bridge_packets)) { - printf("Failed to set bridge_packets for kernel driver\n"); + if (hapd->iconf->ieee80211d && + hostapd_set_ieee80211d(hapd, 1) < 0) { + wpa_printf(MSG_ERROR, "Failed to set ieee80211d (%d)", + hapd->iconf->ieee80211d); return -1; } - if (conf->radius_server_clients) { - struct radius_server_conf srv; - memset(&srv, 0, sizeof(srv)); - srv.client_file = conf->radius_server_clients; - srv.auth_port = conf->radius_server_auth_port; - srv.hostapd_conf = conf; - srv.eap_sim_db_priv = hapd->eap_sim_db_priv; - srv.ssl_ctx = hapd->ssl_ctx; - srv.ipv6 = conf->radius_server_ipv6; - hapd->radius_srv = radius_server_init(&srv); - if (hapd->radius_srv == NULL) { - printf("RADIUS server initialization failed.\n"); - return -1; - } + if (hapd->iconf->bridge_packets != INTERNAL_BRIDGE_DO_NOT_CONTROL && + hostapd_set_internal_bridge(hapd, hapd->iconf->bridge_packets)) { + wpa_printf(MSG_ERROR, "Failed to set bridge_packets for " + "kernel driver"); + return -1; } /* TODO: merge with hostapd_driver_init() ? */ @@ -1420,153 +1574,106 @@ static int setup_interface1(struct hostapd_iface *iface) /* Not all drivers support this yet, so continue without hw * feature data. */ } else { - return hostapd_select_hw_mode_start(iface, - setup_interface2_wrapper); + int ret = hostapd_select_hw_mode(iface); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Could not select hw_mode and " + "channel. (%d)", ret); + return -1; + } } - eloop_register_timeout(0, 0, setup_interface2_handler, iface, NULL); - return 0; -} - - -/** - * setup_interface_start - Handler to start setup interface - * @eloop_data: Stores the struct hostapd_iface * for the interface. - * @user_ctx: Unused. - * - * An eloop handler is used so that all errors can be processed by the - * callback without introducing stack recursion. - */ -static void setup_interface_start(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *iface = eloop_data; + hostapd_flush_old_stations(hapd); + hostapd_set_privacy(hapd, 0); - int ret; + if (hapd->iconf->channel) { + freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); + wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " + "Frequency: %d MHz", + hostapd_hw_mode_txt(hapd->iconf->hw_mode), + hapd->iconf->channel, freq); + + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq, + hapd->iconf->ieee80211n, + hapd->iconf->secondary_channel)) { + wpa_printf(MSG_ERROR, "Could not set channel for " + "kernel driver"); + return -1; + } + } - ret = setup_interface1(iface); - if (ret) - setup_interface_finalize(iface, ret); -} + hostapd_broadcast_wep_clear(hapd); + if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + return -1; + hostapd_set_beacon_int(hapd, hapd->iconf->beacon_int); + ieee802_11_set_beacon(hapd); -/** - * hostapd_setup_interface_start - Start the setup of an interface - * @iface: Pointer to interface data. - * @cb: The function to callback when done. - * Returns: 0 if it starts successfully; cb will be called when done. - * -1 on failure; cb will not be called. - * - * Initializes the driver interface, validates the configuration, - * and sets driver parameters based on the configuration. - * Flushes old stations, sets the channel, DFS parameters, encryption, - * beacons, and WDS links based on the configuration. - */ -int hostapd_setup_interface_start(struct hostapd_iface *iface, - hostapd_iface_cb cb) -{ - if (iface->setup_cb) { - wpa_printf(MSG_DEBUG, - "%s: Interface setup already in progress.\n", - iface->bss[0]->conf->iface); + if (hapd->iconf->rts_threshold > -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + wpa_printf(MSG_ERROR, "Could not set RTS threshold for " + "kernel driver"); return -1; } - iface->setup_cb = cb; - - eloop_register_timeout(0, 0, setup_interface_start, iface, NULL); - - return 0; -} - - -/** - * hostapd_setup_interace_stop - Stops the setup of an interface - * @iface: Pointer to interface data - * Returns: 0 if successfully stopped; - * -1 on failure (i.e., was not in progress) - */ -int hostapd_setup_interface_stop(struct hostapd_iface *iface) -{ - return setup_interface_finalize(iface, -1); -} - - -struct driver { - struct driver *next; - char *name; - const struct driver_ops *ops; -}; -static struct driver *drivers = NULL; + if (hapd->iconf->fragm_threshold > -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " + "for kernel driver"); + return -1; + } -void driver_register(const char *name, const struct driver_ops *ops) -{ - struct driver *d; + prev_addr = hapd->own_addr; - d = malloc(sizeof(struct driver)); - if (d == NULL) { - printf("Failed to register driver %s!\n", name); - return; - } - d->name = strdup(name); - if (d->name == NULL) { - printf("Failed to register driver %s!\n", name); - free(d); - return; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (j) + os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); + if (hostapd_setup_bss(hapd, j == 0)) + return -1; + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + prev_addr = hapd->own_addr; } - d->ops = ops; - d->next = drivers; - drivers = d; -} + hostapd_tx_queue_params(iface); + ap_list_init(iface); -void driver_unregister(const char *name) -{ - struct driver *p, **pp; - - for (pp = &drivers; (p = *pp) != NULL; pp = &p->next) { - if (strcasecmp(p->name, name) == 0) { - *pp = p->next; - p->next = NULL; - free(p->name); - free(p); - break; - } + if (hostapd_driver_commit(hapd) < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to commit driver " + "configuration", __func__); + return -1; } -} - -static void driver_unregister_all(void) -{ - struct driver *p, *pp; - p = drivers; - drivers = NULL; - while (p) { - pp = p; - p = p->next; - free(pp->name); - free(pp); - } + return ret; } -const struct driver_ops * driver_lookup(const char *name) +/** + * hostapd_setup_interface - Setup of an interface + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Initializes the driver interface, validates the configuration, + * and sets driver parameters based on the configuration. + * Flushes old stations, sets the channel, encryption, + * beacons, and WDS links based on the configuration. + */ +static int hostapd_setup_interface(struct hostapd_iface *iface) { - struct driver *p; - - if (strcmp(name, "default") == 0) { - p = drivers; - while (p && p->next) - p = p->next; - return p->ops; - } + int ret; - for (p = drivers; p != NULL; p = p->next) { - if (strcasecmp(p->name, name) == 0) - return p->ops; + ret = setup_interface(iface); + if (ret) { + wpa_printf(MSG_DEBUG, "%s: Unable to setup interface.", + iface->bss[0]->conf->iface); + eloop_terminate(); + return -1; + } else if (!hostapd_drv_none(iface->bss[0])) { + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", + iface->bss[0]->conf->iface); } - return NULL; + return 0; } @@ -1576,7 +1683,7 @@ static void show_version(void) "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> " + "Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> " "and contributors\n"); } @@ -1620,7 +1727,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, { struct hostapd_data *hapd; - hapd = wpa_zalloc(sizeof(*hapd)); + hapd = os_zalloc(sizeof(*hapd)); if (hapd == NULL) return NULL; @@ -1635,29 +1742,31 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && - (hapd->conf->ca_cert || hapd->conf->server_cert)) { + (hapd->conf->ca_cert || hapd->conf->server_cert || + hapd->conf->dh_file)) { struct tls_connection_params params; hapd->ssl_ctx = tls_init(NULL); if (hapd->ssl_ctx == NULL) { - printf("Failed to initialize TLS\n"); + wpa_printf(MSG_ERROR, "Failed to initialize TLS"); goto fail; } - memset(¶ms, 0, sizeof(params)); + os_memset(¶ms, 0, sizeof(params)); params.ca_cert = hapd->conf->ca_cert; params.client_cert = hapd->conf->server_cert; params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; + params.dh_file = hapd->conf->dh_file; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { - printf("Failed to set TLS parameters\n"); + wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); goto fail; } if (tls_global_set_verify(hapd->ssl_ctx, hapd->conf->check_crl)) { - printf("Failed to enable check_crl\n"); + wpa_printf(MSG_ERROR, "Failed to enable check_crl"); goto fail; } } @@ -1669,18 +1778,14 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, eap_sim_db_init(hapd->conf->eap_sim_db, hostapd_sim_db_cb, hapd); if (hapd->eap_sim_db_priv == NULL) { - printf("Failed to initialize EAP-SIM database " - "interface\n"); + wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " + "database interface"); goto fail; } } #endif /* EAP_SERVER */ - if (hapd->conf->assoc_ap) - hapd->assoc_ap_state = WAIT_BEACON; - - /* FIX: need to fix this const vs. not */ - hapd->driver = (struct driver_ops *) hapd->iconf->driver; + hapd->driver = hapd->iconf->driver; return hapd; @@ -1688,7 +1793,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, fail: #endif /* TODO: cleanup allocated resources(?) */ - free(hapd); + os_free(hapd); return NULL; } @@ -1709,11 +1814,11 @@ static struct hostapd_iface * hostapd_init(const char *config_file) struct hostapd_data *hapd; size_t i; - hapd_iface = wpa_zalloc(sizeof(*hapd_iface)); + hapd_iface = os_zalloc(sizeof(*hapd_iface)); if (hapd_iface == NULL) goto fail; - hapd_iface->config_fname = strdup(config_file); + hapd_iface->config_fname = os_strdup(config_file); if (hapd_iface->config_fname == NULL) goto fail; @@ -1723,8 +1828,8 @@ static struct hostapd_iface * hostapd_init(const char *config_file) hapd_iface->conf = conf; hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = wpa_zalloc(conf->num_bss * - sizeof(struct hostapd_data *)); + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) goto fail; @@ -1748,48 +1853,24 @@ fail: tls_deinit(hapd->ssl_ctx); } - free(hapd_iface->config_fname); - free(hapd_iface->bss); - free(hapd_iface); + os_free(hapd_iface->config_fname); + os_free(hapd_iface->bss); + os_free(hapd_iface); } return NULL; } -/** - * register_drivers - Register driver interfaces - * - * This function is generated by Makefile (into driver_conf.c) to call all - * configured driver interfaces to register them to core hostapd. - */ -void register_drivers(void); - - -/** - * setup_interface_done - Callback when an interface is done being setup. - * @iface: Pointer to interface data. - * @status: Status of the interface setup (0 on success; -1 on failure). - */ -static void setup_interface_done(struct hostapd_iface *iface, int status) -{ - if (status) { - wpa_printf(MSG_DEBUG, "%s: Unable to setup interface.", - iface->bss[0]->conf->iface); - eloop_terminate(); - } else - wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", - iface->bss[0]->conf->iface); -} - - int main(int argc, char *argv[]) { struct hapd_interfaces interfaces; int ret = 1, k; size_t i, j; - int c, debug = 0, daemonize = 0; + int c, debug = 0, daemonize = 0, tnc = 0; const char *pid_file = NULL; + hostapd_logger_register_cb(hostapd_logger_cb); + for (;;) { c = getopt(argc, argv, "BdhKP:tv"); if (c < 0) @@ -1829,8 +1910,6 @@ int main(int argc, char *argv[]) if (optind == argc) usage(); - register_drivers(); /* NB: generated by Makefile */ - if (eap_server_register_methods()) { wpa_printf(MSG_ERROR, "Failed to register EAP methods"); return -1; @@ -1838,11 +1917,11 @@ int main(int argc, char *argv[]) interfaces.count = argc - optind; - interfaces.iface = malloc(interfaces.count * - sizeof(struct hostapd_iface *)); + interfaces.iface = os_malloc(interfaces.count * + sizeof(struct hostapd_iface *)); if (interfaces.iface == NULL) { - printf("malloc failed\n"); - exit(1); + wpa_printf(MSG_ERROR, "malloc failed\n"); + return -1; } if (eloop_init(&interfaces)) { @@ -1858,7 +1937,8 @@ int main(int argc, char *argv[]) /* Initialize interfaces */ for (i = 0; i < interfaces.count; i++) { - printf("Configuration file: %s\n", argv[optind + i]); + wpa_printf(MSG_ERROR, "Configuration file: %s", + argv[optind + i]); interfaces.iface[i] = hostapd_init(argv[optind + i]); if (!interfaces.iface[i]) goto out; @@ -1867,14 +1947,24 @@ int main(int argc, char *argv[]) logger_stdout_level > 0) interfaces.iface[i]->bss[0]->conf-> logger_stdout_level--; - interfaces.iface[i]->bss[0]->conf->debug++; } - ret = hostapd_setup_interface_start(interfaces.iface[i], - setup_interface_done); + ret = hostapd_setup_interface(interfaces.iface[i]); if (ret) goto out; + + for (k = 0; k < (int) interfaces.iface[i]->num_bss; k++) { + if (interfaces.iface[i]->bss[0]->conf->tnc) + tnc++; + } + } + +#ifdef EAP_TNC + if (tnc && tncs_global_init() < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); + goto out; } +#endif /* EAP_TNC */ if (daemonize && os_daemonize(pid_file)) { perror("daemon"); @@ -1904,7 +1994,6 @@ int main(int argc, char *argv[]) for (i = 0; i < interfaces.count; i++) { if (!interfaces.iface[i]) continue; - hostapd_setup_interface_stop(interfaces.iface[i]); hostapd_cleanup_iface_pre(interfaces.iface[i]); for (j = 0; j < interfaces.iface[i]->num_bss; j++) { struct hostapd_data *hapd = @@ -1915,10 +2004,14 @@ int main(int argc, char *argv[]) hostapd_driver_deinit(hapd); } for (j = 0; j < interfaces.iface[i]->num_bss; j++) - free(interfaces.iface[i]->bss[j]); + os_free(interfaces.iface[i]->bss[j]); hostapd_cleanup_iface(interfaces.iface[i]); } - free(interfaces.iface); + os_free(interfaces.iface); + +#ifdef EAP_TNC + tncs_global_deinit(); +#endif /* EAP_TNC */ eloop_destroy(); @@ -1928,8 +2021,6 @@ int main(int argc, char *argv[]) eap_server_unregister_methods(); - driver_unregister_all(); - os_daemonize_terminate(pid_file); return ret; diff --git a/contrib/hostapd/hostapd.conf b/hostapd/hostapd.conf index d8ebe164f9270..0602cb04a50c5 100644 --- a/contrib/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -5,12 +5,15 @@ # management frames); ath0 for madwifi interface=wlan0 -# In case of madwifi driver, an additional configuration parameter, bridge, -# must be used to notify hostapd if the interface is included in a bridge. This -# parameter is not used with Host AP driver. +# In case of madwifi and nl80211 driver interfaces, an additional configuration +# parameter, bridge, must be used to notify hostapd if the interface is +# included in a bridge. This parameter is not used with Host AP driver. #bridge=br0 -# Driver interface type (hostap/wired/madwifi/prism54; default: hostap) +# Driver interface type (hostap/wired/madwifi/prism54/test/none/nl80211/bsd); +# default: hostap). nl80211 is used with all Linux mac80211 drivers. +# Use driver=none if building hostapd as a standalone RADIUS server that does +# not control any wireless/wired driver. # driver=hostap # hostapd event logger configuration @@ -40,9 +43,6 @@ logger_syslog_level=2 logger_stdout=-1 logger_stdout_level=2 -# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive -debug=0 - # Dump file for state information (on SIGUSR1) dump_file=/tmp/hostapd.dump @@ -77,10 +77,9 @@ ctrl_interface_group=0 # SSID to be used in IEEE 802.11 management frames ssid=test -# Country code (ISO/IEC 3166-1). Used to set regulatory domain. -# Modify as needed to indicate country in which device is operating. +# Country code (ISO/IEC 3166-1). Used to set regulatory domain. +# Set as needed to indicate country in which device is operating. # This can limit available channels and transmit power. -# (default: US) #country_code=US # Enable IEEE 802.11d. This advertises the country_code and the set of allowed @@ -90,26 +89,15 @@ ssid=test # (default: 0 = disabled) #ieee80211d=1 -# Enable IEEE 802.11h. This enables the TPC and DFS services when operating -# in a regulatory domain which requires them. Once enabled it will be -# operational only when working in hw_mode a and in countries where it is -# required. The end user should not be allowed to disable this. -# The country_code setting must be configured with the correct country for -# IEEE 802.11h to function. -# When IEEE 802.11h is operational, the channel_policy and configured channel -# settings will be ignored but will behave as though the channel_policy is -# set to "3" (automatic channel selection). When IEEE 802.11h is enabled but -# not operational (for example, if the radio mode is changed from "a" to "b") -# the channel_policy and channel settings take effect again. -# (default: 1 = enabled) -#ieee80211h=1 - # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, # Default: IEEE 802.11b hw_mode=a # Channel number (IEEE 802.11) -# (default: 0, i.e., not set, used with channel_policy=2) +# (default: 0, i.e., not set) +# Please note that some drivers (e.g., madwifi) do not use this value from +# hostapd and the channel will need to be configuration separately with +# iwconfig. channel=60 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) @@ -157,10 +145,21 @@ fragm_threshold=2346 #basic_rates=10 20 55 110 #basic_rates=60 120 240 +# Short Preamble +# This parameter can be used to enable optional use of short preamble for +# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. +# This applies only to IEEE 802.11b-compatible networks and this should only be +# enabled if the local hardware supports use of short preamble. If any of the +# associated STAs do not support short preamble, use of short preamble will be +# disabled (and enabled when such STAs disassociate) dynamically. +# 0 = do not allow use of short preamble (default) +# 1 = allow use of short preamble +#preamble=1 + # Station MAC address -based authentication # Please note that this kind of access control requires a driver that uses # hostapd to take care of management frame processing and as such, this can be -# used with driver=hostap or driver=devicescape, but not with driver=madwifi. +# used with driver=hostap or driver=nl80211, but not with driver=madwifi. # 0 = accept unless in deny list # 1 = deny unless in accept list # 2 = use external RADIUS server (accept/deny lists are searched first) @@ -308,10 +307,6 @@ wme_ac_vo_txop_limit=47 wme_ac_vo_acm=0 # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 -# Associate as a station to another AP while still acting as an AP on the same -# channel. -#assoc_ap_addr=00:12:34:56:78:9a - # Static WEP key configuration # # The key number to use when transmitting. @@ -371,6 +366,47 @@ wme_ac_vo_acm=0 # default: do not control from hostapd (80211.o defaults to 1=enabled) #bridge_packets=1 +# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to +# remain asleep). Default: 65535 (no limit apart from field size) +#max_listen_interval=100 + +##### IEEE 802.11n related configuration ###################################### + +# ieee80211n: Whether IEEE 802.11n (HT) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211n=1 + +# ht_capab: HT capabilities (list of flags) +# LDPC coding capability: [LDPC] = supported +# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary +# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz +# with secondary channel below the primary channel +# (20 MHz only if neither is set) +# Note: There are limits on which channels can be used with HT40- and +# HT40+. Following table shows the channels that may be available for +# HT40- and HT40+ use per IEEE 802.11n Annex J: +# freq HT40- HT40+ +# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan) +# 5 GHz 40,48,56,64 36,44,52,60 +# (depending on the location, not all of these channels may be available +# for use) +# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC] +# (SMPS disabled if neither is set) +# HT-greenfield: [GF] (disabled if not set) +# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set) +# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set) +# Tx STBC: [TX-STBC] (disabled if not set) +# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial +# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC +# disabled if none of these set +# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set) +# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not +# set) +# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set) +# PSMP support: [PSMP] (disabled if not set) +# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set) +#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40] ##### IEEE 802.1X-2004 related configuration ################################## @@ -457,6 +493,18 @@ eap_server=0 # 2 = check all CRLs in the certificate path #check_crl=1 +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA authentication does +# not use this configuration. However, it is possible setup RSA to use +# ephemeral DH key exchange. In addition, ciphers with DSA keys always use +# ephemeral DH keys. This can be used to achieve forward secrecy. If the file +# is in DSA parameters format, it will be automatically converted into DH +# params. This parameter is required if anonymous EAP-FAST is used. +# You can generate DH parameters file with OpenSSL, e.g., +# "openssl dhparam -out /etc/hostapd.dh.pem 1024" +#dh_file=/etc/hostapd.dh.pem + # Configuration data for EAP-SIM database/authentication gateway interface. # This is a text string in implementation specific format. The example # implementation in eap_sim_db.c uses this as the UNIX domain socket name for @@ -464,6 +512,51 @@ eap_server=0 # prefix. #eap_sim_db=unix:/tmp/hlr_auc_gw.sock +# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, +# random value. It is configured as a 16-octet value in hex format. It can be +# generated, e.g., with the following command: +# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' ' +#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f + +# EAP-FAST authority identity (A-ID) +# A-ID indicates the identity of the authority that issues PACs. The A-ID +# should be unique across all issuing servers. In theory, this is a variable +# length field, but due to some existing implementations required A-ID to be +# 16 octets in length, it is strongly recommended to use that length for the +# field to provided interoperability with deployed peer implementation. This +# field is configured in hex format. +#eap_fast_a_id=101112131415161718191a1b1c1d1e1f + +# EAP-FAST authority identifier information (A-ID-Info) +# This is a user-friendly name for the A-ID. For example, the enterprise name +# and server name in a human-readable format. This field is encoded as UTF-8. +#eap_fast_a_id_info=test server + +# Enable/disable different EAP-FAST provisioning modes: +#0 = provisioning disabled +#1 = only anonymous provisioning allowed +#2 = only authenticated provisioning allowed +#3 = both provisioning modes allowed (default) +#eap_fast_prov=3 + +# EAP-FAST PAC-Key lifetime in seconds (hard limit) +#pac_key_lifetime=604800 + +# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard +# limit). The server will generate a new PAC-Key when this number of seconds +# (or fewer) of the lifetime remains. +#pac_key_refresh_time=86400 + +# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND +# (default: 0 = disabled). +#eap_sim_aka_result_ind=1 + +# Trusted Network Connect (TNC) +# If enabled, TNC validation will be required before the peer is allowed to +# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other +# EAP method is enabled, the peer will be allowed to connect without TNC. +#tnc=1 + ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### @@ -481,6 +574,8 @@ own_ip_addr=127.0.0.1 # Optional NAS-Identifier string for RADIUS messages. When used, this should be # a unique to the NAS within the scope of the RADIUS server. For example, a # fully qualified domain name can be used here. +# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and +# 48 octets long. #nas_identifier=ap.example.com # RADIUS authentication server @@ -527,7 +622,8 @@ own_ip_addr=127.0.0.1 # attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN), # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value # VLANID as a string). vlan_file option below must be configured if dynamic -# VLANs are used. +# VLANs are used. Optionally, the local MAC ACL list (accept_mac_file) can be +# used to set static client MAC address to VLAN ID mapping. # 0 = disabled (default) # 1 = option; use default interface if RADIUS server does not include VLAN ID # 2 = required; reject authentication if RADIUS server does not include VLAN ID @@ -552,7 +648,7 @@ own_ip_addr=127.0.0.1 ##### RADIUS authentication server configuration ############################## # hostapd can be used as a RADIUS authentication server for other hosts. This -# requires that the integrated EAP authenticator is also enabled and both +# requires that the integrated EAP server is also enabled and both # authentication services are sharing the same configuration. # File name of the RADIUS clients configuration for the RADIUS server. If this @@ -596,7 +692,8 @@ own_ip_addr=127.0.0.1 #wpa_psk_file=/etc/hostapd.wpa_psk # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The -# entries are separated with a space. +# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be +# added to enable SHA256-based stronger algorithms. # (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP @@ -609,7 +706,10 @@ own_ip_addr=127.0.0.1 # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, # TKIP will be used as the group cipher. # (dot11RSNAConfigPairwiseCiphersTable) +# Pairwise cipher for WPA (v1) (default: TKIP) #wpa_pairwise=TKIP CCMP +# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) +#rsn_pairwise=CCMP # Time interval for rekeying GTK (broadcast/multicast encryption keys) in # seconds. (dot11RSNAConfigGroupRekeyTime) @@ -623,6 +723,10 @@ own_ip_addr=127.0.0.1 # (in seconds). #wpa_gmk_rekey=86400 +# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of +# PTK to mitigate some attacks against TKIP deficiencies. +#wpa_ptk_rekey=600 + # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN # authentication and key handshake before actually associating with a new AP. @@ -644,12 +748,78 @@ own_ip_addr=127.0.0.1 # 1 = enabled #peerkey=1 -# ieee80211w: Whether management frame protection is enabled +# ieee80211w: Whether management frame protection (MFP) is enabled # 0 = disabled (default) # 1 = optional # 2 = required #ieee80211w=0 +# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP) +# (maximum time to wait for a SA Query response) +# dot11AssociationSAQueryMaximumTimeout, 1...4294967295 +#assoc_sa_query_max_timeout=1000 + +# Association SA Query retry timeout (in TU = 1.024 ms; for MFP) +# (time between two subsequent SA Query requests) +# dot11AssociationSAQueryRetryTimeout, 1...4294967295 +#assoc_sa_query_retry_timeout=201 + + +# okc: Opportunistic Key Caching (aka Proactive Key Caching) +# Allow PMK cache to be shared opportunistically among configured interfaces +# and BSSes (i.e., all configurations within a single hostapd process). +# 0 = disabled (default) +# 1 = enabled +#okc=1 + + +##### IEEE 802.11r configuration ############################################## + +# Mobility Domain identifier (dot11FTMobilityDomainID, MDID) +# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the +# same SSID) between which a STA can use Fast BSS Transition. +# 2-octet identifier as a hex string. +#mobility_domain=a1b2 + +# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID) +# 1 to 48 octet identifier. +# This is configured with nas_identifier (see RADIUS client section above). + +# Default lifetime of the PMK-RO in minutes; range 1..65535 +# (dot11FTR0KeyLifetime) +#r0_key_lifetime=10000 + +# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) +# 6-octet identifier as a hex string. +#r1_key_holder=000102030405 + +# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) +# (dot11FTReassociationDeadline) +#reassociation_deadline=1000 + +# List of R0KHs in the same Mobility Domain +# format: <MAC address> <NAS Identifier> <128-bit key as hex string> +# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC +# address when requesting PMK-R1 key from the R0KH that the STA used during the +# Initial Mobility Domain Association. +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff +# And so on.. One line per R0KH. + +# List of R1KHs in the same Mobility Domain +# format: <MAC address> <R0KH-ID> <128-bit key as hex string> +# This list is used to map R1KH-ID to a destination MAC address when sending +# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD +# that can request PMK-R1 keys. +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff +# And so on.. One line per R1KH. + +# Whether PMK-R1 push is enabled at R0KH +# 0 = do not push PMK-R1 to all configured R1KHs (default) +# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived +#pmk_r1_push=1 + ##### Passive scanning ######################################################## # Scan different channels every N seconds. 0 = disable passive scanning. #passive_scan_interval=60 @@ -683,7 +853,146 @@ own_ip_addr=127.0.0.1 # default: 60 #ap_table_expiration_time=3600 -# Multiple BSSID support + +##### Wi-Fi Protected Setup (WPS) ############################################# + +# WPS state +# 0 = WPS disabled (default) +# 1 = WPS enabled, not configured +# 2 = WPS enabled, configured +#wps_state=2 + +# AP can be configured into a locked state where new WPS Registrar are not +# accepted, but previously authorized Registrars (including the internal one) +# can continue to add new Enrollees. +#ap_setup_locked=1 + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# This value is used as the UUID for the internal WPS Registrar. If the AP +# is also using UPnP, this value should be set to the device's UPnP UUID. +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs +# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the +# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of +# per-device PSKs is recommended as the more secure option (i.e., make sure to +# set wpa_psk_file when using WPS with WPA-PSK). + +# When an Enrollee requests access to the network with PIN method, the Enrollee +# PIN will need to be entered for the Registrar. PIN request notifications are +# sent to hostapd ctrl_iface monitor. In addition, they can be written to a +# text file that could be used, e.g., to populate the AP administration UI with +# pending PIN requests. If the following variable is set, the PIN requests will +# be written to the configured file. +#wps_pin_requests=/var/run/hostapd_wps_pin_requests + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless AP + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=WAP + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: <categ>-<OUI>-<subcateg> +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=6-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Config Methods +# List of the supported configuration methods +#config_methods=label display push_button keypad + +# Access point PIN for initial configuration and adding Registrars +# If not set, hostapd will not allow external WPS Registrars to control the +# access point. +#ap_pin=12345670 + +# Skip building of automatic WPS credential +# This can be used to allow the automatically generated Credential attribute to +# be replaced with pre-configured Credential(s). +#skip_cred_build=1 + +# Additional Credential attribute(s) +# This option can be used to add pre-configured Credential attributes into M8 +# message when acting as a Registrar. If skip_cred_build=1, this data will also +# be able to override the Credential attribute that would have otherwise been +# automatically generated based on network configuration. This configuration +# option points to an external file that much contain the WPS Credential +# attribute(s) as binary data. +#extra_cred=hostapd.cred + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and +# extra_cred be used to provide the Credential data for Enrollees. +# +# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file +# both for Credential processing and for marking AP Setup Locked based on +# validation failures of AP PIN. An external program is responsible on updating +# the configuration appropriately in this case. +#wps_cred_processing=0 + +# AP Settings Attributes for M7 +# By default, hostapd generates the AP Settings Attributes for M7 based on the +# current configuration. It is possible to override this by providing a file +# with pre-configured attributes. This is similar to extra_cred file format, +# but the AP Settings attributes are not encapsulated in a Credential +# attribute. +#ap_settings=hostapd.ap_settings + +# WPS UPnP interface +# If set, support for external Registrars is enabled. +#upnp_iface=br0 + +# Friendly Name (required for UPnP) +# Short description for end use. Should be less than 64 characters. +#friendly_name=WPS Access Point + +# Manufacturer URL (optional for UPnP) +#manufacturer_url=http://www.example.com/ + +# Model Description (recommended for UPnP) +# Long description for end user. Should be less than 128 characters. +#model_description=Wireless Access Point + +# Model URL (optional for UPnP) +#model_url=http://www.example.com/model/ + +# Universal Product Code (optional for UPnP) +# 12-digit, all-numeric code that identifies the consumer package. +#upc=123456789012 + +##### Multiple BSSID support ################################################## # # Above configuration is using the default interface (wlan#, or multi-SSID VLAN # interfaces). Other BSSIDs can be added by using separator 'bss' with diff --git a/contrib/hostapd/hostapd.deny b/hostapd/hostapd.deny index 1616678f579e3..1616678f579e3 100644 --- a/contrib/hostapd/hostapd.deny +++ b/hostapd/hostapd.deny diff --git a/contrib/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user index b9d7f8b08ac6e..ac9a5d896a049 100644 --- a/contrib/hostapd/hostapd.eap_user +++ b/hostapd/hostapd.eap_user @@ -1,4 +1,4 @@ -# hostapd user database for integrated EAP authenticator +# hostapd user database for integrated EAP server # Each line must contain an identity, EAP method(s), and an optional password # separated with whitespace (space or tab). The identity and password must be @@ -16,10 +16,11 @@ # access is needed, two user entries is needed, one for phase 1 and another # with the same username for phase 2. # -# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-SIM, and EAP-AKA do not use password option. +# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-FAST, EAP-SIM, and EAP-AKA do not use +# password option. # EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a # password. -# EAP-PEAP and EAP-TTLS require Phase 2 configuration. +# EAP-PEAP, EAP-TTLS, and EAP-FAST require Phase 2 configuration. # # * can be used as a wildcard to match any user identity. The main purposes for # this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to @@ -40,6 +41,13 @@ # version based on the Phase 1 identity. Without this flag, the EAP # authenticator advertises the highest supported version and select the version # based on the first PEAP packet from the supplicant. +# +# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel. +# Tunneled EAP methods are configured with standard EAP method name and [2] +# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP, +# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a +# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password +# hash. # Phase 1 users "user" MD5 "password" @@ -72,3 +80,12 @@ "not anonymous" MSCHAPV2 "password" [2] "user" MD5,GTC,MSCHAPV2 "password" [2] "test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2] +"ttls-user" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2 "password" [2] + +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2 +"0"* AKA [2] +"1"* SIM [2] +"2"* AKA [2] +"3"* SIM [2] +"4"* AKA [2] +"5"* SIM [2] diff --git a/contrib/hostapd/hostapd.h b/hostapd/hostapd.h index 70f802d2a8f09..26f30d7016ff7 100644 --- a/contrib/hostapd/hostapd.h +++ b/hostapd/hostapd.h @@ -1,7 +1,8 @@ /* * hostapd / Initialization and configuration * Host AP kernel driver - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -31,15 +32,13 @@ #ifndef ETH_P_PAE #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #endif /* ETH_P_PAE */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ -#ifndef BIT -#define BIT(x) (1 << (x)) -#endif - -#ifndef MAC2STR -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" -#endif +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ #include "config.h" @@ -57,12 +56,12 @@ struct ieee8023_hdr { struct ieee80211_hdr { - u16 frame_control; - u16 duration_id; + le16 frame_control; + le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; - u16 seq_ctrl; + le16 seq_ctrl; /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame */ } STRUCT_PACKED; @@ -98,9 +97,10 @@ struct hostap_sta_driver_data { int last_ack_rssi; }; -struct driver_ops; +struct wpa_driver_ops; struct wpa_ctrl_dst; struct radius_server_data; +struct upnp_wps_device_sm; #ifdef CONFIG_FULL_DYNAMIC_VLAN struct full_dynamic_vlan; @@ -127,7 +127,8 @@ struct hostapd_data { */ struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; - struct driver_ops *driver; + const struct wpa_driver_ops *driver; + void *drv_priv; u8 *default_wep_key; u8 default_wep_key_idx; @@ -138,16 +139,11 @@ struct hostapd_data { struct iapp_data *iapp; - enum { DO_NOT_ASSOC = 0, WAIT_BEACON, AUTHENTICATE, ASSOCIATE, - ASSOCIATED } assoc_ap_state; - char assoc_ap_ssid[33]; - int assoc_ap_ssid_len; - u16 assoc_ap_aid; - struct hostapd_cached_radius_acl *acl_cache; struct hostapd_acl_query_data *acl_queries; struct wpa_authenticator *wpa_auth; + struct eapol_authenticator *eapol_auth; struct rsn_preauth_interface *preauth_iface; time_t michael_mic_failure; @@ -166,18 +162,20 @@ struct hostapd_data { #ifdef CONFIG_FULL_DYNAMIC_VLAN struct full_dynamic_vlan *full_dynamic_vlan; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ -}; - - -/** - * hostapd_iface_cb - Generic callback type for per-iface asynchronous requests - * @iface: the interface the event occured on. - * @status: 0 if the request succeeded; -1 if the request failed. - */ -typedef void (*hostapd_iface_cb)(struct hostapd_iface *iface, int status); + struct l2_packet_data *l2; + struct wps_context *wps; + +#ifdef CONFIG_WPS + u8 *wps_beacon_ie; + size_t wps_beacon_ie_len; + u8 *wps_probe_resp_ie; + size_t wps_probe_resp_ie_len; + unsigned int ap_pin_failures; + struct upnp_wps_device_sm *wps_upnp; +#endif /* CONFIG_WPS */ +}; -struct hostapd_config_change; /** * struct hostapd_iface - hostapd per-interface data structure @@ -186,8 +184,6 @@ struct hostapd_iface { char *config_fname; struct hostapd_config *conf; - hostapd_iface_cb setup_cb; - size_t num_bss; struct hostapd_data **bss; @@ -203,7 +199,6 @@ struct hostapd_iface { * current_mode->channels */ int num_rates; struct hostapd_rate_data *current_rates; - hostapd_iface_cb hw_mode_sel_cb; u16 hw_flags; @@ -219,37 +214,25 @@ struct hostapd_iface { int olbc; /* Overlapping Legacy BSS Condition */ - int dfs_enable; - u8 pwr_const; - unsigned int tx_power; - unsigned int sta_max_power; + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; - unsigned int channel_switch; + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; - struct hostapd_config_change *change; - hostapd_iface_cb reload_iface_cb; - hostapd_iface_cb config_reload_cb; + /* Overlapping BSS information */ + int olbc_ht; + +#ifdef CONFIG_IEEE80211N + u16 ht_op_mode; +#endif /* CONFIG_IEEE80211N */ }; void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); -void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, - unsigned int module, int level, const char *fmt, - ...) PRINTF_FORMAT(5, 6); - - -#ifndef _MSC_VER -#define HOSTAPD_DEBUG(level, args...) \ -do { \ - if (hapd->conf == NULL || hapd->conf->debug >= (level)) \ - printf(args); \ -} while (0) -#endif /* _MSC_VER */ - -#define HOSTAPD_DEBUG_COND(level) (hapd->conf->debug >= (level)) - -const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, - size_t buflen); -int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b); +int hostapd_reload_config(struct hostapd_iface *iface); #endif /* HOSTAPD_H */ diff --git a/contrib/hostapd/hostapd.radius_clients b/hostapd/hostapd.radius_clients index 3980427253b4c..3980427253b4c 100644 --- a/contrib/hostapd/hostapd.radius_clients +++ b/hostapd/hostapd.radius_clients diff --git a/contrib/hostapd/hostapd.sim_db b/hostapd/hostapd.sim_db index 01c593de8d2da..01c593de8d2da 100644 --- a/contrib/hostapd/hostapd.sim_db +++ b/hostapd/hostapd.sim_db diff --git a/contrib/hostapd/hostapd.vlan b/hostapd/hostapd.vlan index 98254fa84f016..98254fa84f016 100644 --- a/contrib/hostapd/hostapd.vlan +++ b/hostapd/hostapd.vlan diff --git a/contrib/hostapd/hostapd.wpa_psk b/hostapd/hostapd.wpa_psk index 0a9499acd7366..0a9499acd7366 100644 --- a/contrib/hostapd/hostapd.wpa_psk +++ b/hostapd/hostapd.wpa_psk diff --git a/contrib/hostapd/hostapd_cli.1 b/hostapd/hostapd_cli.1 index 8d6587fc5c481..8d6587fc5c481 100644 --- a/contrib/hostapd/hostapd_cli.1 +++ b/hostapd/hostapd_cli.1 diff --git a/contrib/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 870f221bb5c3b..26141130d81cf 100644 --- a/contrib/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,12 +16,13 @@ #include <dirent.h> #include "wpa_ctrl.h" +#include "common.h" #include "version.h" static const char *hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> and contributors"; static const char *hostapd_cli_license = @@ -82,6 +83,13 @@ static const char *commands_help = " sta <addr> get MIB variables for one station\n" " all_sta get MIB variables for all stations\n" " new_sta <addr> add a new station\n" +#ifdef CONFIG_IEEE80211W +" sa_query <addr> send SA Query to a station\n" +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS +" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n" +" wps_pbc indicate button pushed to initiate PBC\n" +#endif /* CONFIG_WPS */ " help show this usage help\n" " interface [ifname] show interfaces/select interface\n" " level <debug level> change debug level\n" @@ -93,6 +101,7 @@ static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; static const char *ctrl_iface_dir = "/var/run/hostapd"; static char *ctrl_ifname = NULL; +static int ping_interval = 5; static void usage(void) @@ -101,7 +110,8 @@ static void usage(void) fprintf(stderr, "\n" "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] " - "[command..]\n" + "[-G<ping interval>] \\\n" + " [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" @@ -229,6 +239,45 @@ static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, } +#ifdef CONFIG_IEEE80211W +static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'sa_query' command - exactly one argument, " + "STA address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WPS +static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 2) { + printf("Invalid 'wps_pin' command - exactly two arguments, " + "UUID and PIN, are required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_PBC"); +} +#endif /* CONFIG_WPS */ + + static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, char *addr, size_t addr_len) { @@ -260,7 +309,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, while (*pos != '\0' && *pos != '\n') pos++; *pos = '\0'; - snprintf(addr, addr_len, "%s", buf); + os_strlcpy(addr, buf, addr_len); return 0; } @@ -377,6 +426,13 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "sta", hostapd_cli_cmd_sta }, { "all_sta", hostapd_cli_cmd_all_sta }, { "new_sta", hostapd_cli_cmd_new_sta }, +#ifdef CONFIG_IEEE80211W + { "sa_query", hostapd_cli_cmd_sa_query }, +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + { "wps_pin", hostapd_cli_cmd_wps_pin }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc }, +#endif /* CONFIG_WPS */ { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, { "level", hostapd_cli_cmd_level }, @@ -453,7 +509,7 @@ static void hostapd_cli_interactive(void) do { hostapd_cli_recv_pending(ctrl_conn, 0); printf("> "); - alarm(1); + alarm(ping_interval); res = fgets(cmd, sizeof(cmd), stdin); alarm(0); if (res == NULL) @@ -515,7 +571,7 @@ static void hostapd_cli_alarm(int sig) } if (ctrl_conn) hostapd_cli_recv_pending(ctrl_conn, 1); - alarm(1); + alarm(ping_interval); } @@ -526,10 +582,13 @@ int main(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "hi:p:v"); + c = getopt(argc, argv, "hG:i:p:v"); if (c < 0) break; switch (c) { + case 'G': + ping_interval = atoi(optarg); + break; case 'h': usage(); return 0; diff --git a/hostapd/hw_features.c b/hostapd/hw_features.c new file mode 100644 index 0000000000000..f1288e0931af2 --- /dev/null +++ b/hostapd/hw_features.c @@ -0,0 +1,487 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "hw_features.h" +#include "driver.h" +#include "config.h" + + +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +int hostapd_get_hw_features(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int ret = 0, i, j; + u16 num_modes, flags; + struct hostapd_hw_modes *modes; + + if (hostapd_drv_none(hapd)) + return -1; + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + if (modes == NULL) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Fetching hardware channel/rate support not " + "supported."); + return -1; + } + + iface->hw_flags = flags; + + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = modes; + iface->num_hw_features = num_modes; + + for (i = 0; i < num_modes; i++) { + struct hostapd_hw_modes *feature = &modes[i]; + /* set flag for channels we can use in current regulatory + * domain */ + for (j = 0; j < feature->num_channels; j++) { + /* + * Disable all channels that are marked not to allow + * IBSS operation or active scanning. In addition, + * disable all channels that require radar detection, + * since that (in addition to full DFS) is not yet + * supported. + */ + if (feature->channels[j].flag & + (HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_RADAR)) + feature->channels[j].flag |= + HOSTAPD_CHAN_DISABLED; + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " + "chan=%d freq=%d MHz max_tx_power=%d dBm", + feature->mode, + feature->channels[j].chan, + feature->channels[j].freq, + feature->channels[j].max_tx_power); + } + } + + return ret; +} + + +static int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode) +{ + int i, num_basic_rates = 0; + int basic_rates_a[] = { 60, 120, 240, -1 }; + int basic_rates_b[] = { 10, 20, -1 }; + int basic_rates_g[] = { 10, 20, 55, 110, -1 }; + int *basic_rates; + + if (hapd->iconf->basic_rates) + basic_rates = hapd->iconf->basic_rates; + else switch (mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + basic_rates = basic_rates_a; + break; + case HOSTAPD_MODE_IEEE80211B: + basic_rates = basic_rates_b; + break; + case HOSTAPD_MODE_IEEE80211G: + basic_rates = basic_rates_g; + break; + default: + return -1; + } + + if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, + basic_rates, mode->mode)) { + wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " + "module"); + } + + os_free(hapd->iface->current_rates); + hapd->iface->num_rates = 0; + + hapd->iface->current_rates = + os_malloc(mode->num_rates * sizeof(struct hostapd_rate_data)); + if (!hapd->iface->current_rates) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " + "table."); + return -1; + } + + for (i = 0; i < mode->num_rates; i++) { + struct hostapd_rate_data *rate; + + if (hapd->iconf->supported_rates && + !hostapd_rate_found(hapd->iconf->supported_rates, + mode->rates[i].rate)) + continue; + + rate = &hapd->iface->current_rates[hapd->iface->num_rates]; + os_memcpy(rate, &mode->rates[i], + sizeof(struct hostapd_rate_data)); + if (hostapd_rate_found(basic_rates, rate->rate)) { + rate->flags |= HOSTAPD_RATE_BASIC; + num_basic_rates++; + } else + rate->flags &= ~HOSTAPD_RATE_BASIC; + wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", + hapd->iface->num_rates, rate->rate, rate->flags); + hapd->iface->num_rates++; + } + + if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { + wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " + "rate sets (%d,%d).", + hapd->iface->num_rates, num_basic_rates); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211N +static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) +{ + int sec_chan, ok, j, first; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + size_t k; + + if (!iface->conf->secondary_channel) + return 1; /* HT40 not used */ + + sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; + wpa_printf(MSG_DEBUG, "HT40: control channel: %d " + "secondary channel: %d", + iface->conf->channel, sec_chan); + + /* Verify that HT40 secondary channel is an allowed 20 MHz + * channel */ + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + chan->chan == sec_chan) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", + sec_chan); + return 0; + } + + /* + * Verify that HT40 primary,secondary channel pair is allowed per + * IEEE 802.11n Annex J. This is only needed for 5 GHz band since + * 2.4 GHz rules allow all cases where the secondary channel fits into + * the list of allowed channels (already checked above). + */ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return 1; + + if (iface->conf->secondary_channel > 0) + first = iface->conf->channel; + else + first = sec_chan; + + ok = 0; + for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { + if (first == allowed[k]) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", + iface->conf->channel, + iface->conf->secondary_channel); + return 0; + } + + return 1; +} + + +static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) +{ + u16 hw = iface->current_mode->ht_capab; + u16 conf = iface->conf->ht_capab; + + if (!iface->conf->ieee80211n) + return 1; + + if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && + !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LDPC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [HT40*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && + (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SMPS-*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_GREEN_FIELD) && + !(hw & HT_CAP_INFO_GREEN_FIELD)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [GF]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-20]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [TX-STBC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_RX_STBC_MASK) > + (hw & HT_CAP_INFO_RX_STBC_MASK)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [RX-STBC*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DELAYED_BA) && + !(hw & HT_CAP_INFO_DELAYED_BA)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DELAYED-BA]"); + return 0; + } + + if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && + !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [MAX-AMSDU-7935]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && + !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DSSS_CCK-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [PSMP]"); + return 0; + } + + if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && + !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LSIG-TXOP-PROT]"); + return 0; + } + + return 1; +} +#endif /* CONFIG_IEEE80211N */ + + +/** + * hostapd_select_hw_mode - Select the hardware mode + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Sets up the hardware mode, channel, rates, and passive scanning + * based on the configuration. + */ +int hostapd_select_hw_mode(struct hostapd_iface *iface) +{ + int i, j, ok, ret; + + if (iface->num_hw_features < 1) + return -1; + + iface->current_mode = NULL; + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = &iface->hw_features[i]; + if (mode->mode == (int) iface->conf->hw_mode) { + iface->current_mode = mode; + break; + } + } + + if (iface->current_mode == NULL) { + wpa_printf(MSG_ERROR, "Hardware does not support configured " + "mode"); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode " + "(%d)", (int) iface->conf->hw_mode); + return -1; + } + + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->chan == iface->conf->channel)) { + ok = 1; + break; + } + } + if (ok == 0 && iface->conf->channel != 0) { + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured channel (%d) not found from the " + "channel list of current mode (%d) %s", + iface->conf->channel, + iface->current_mode->mode, + hostapd_hw_mode_txt(iface->current_mode->mode)); + iface->current_mode = NULL; + } + + if (iface->current_mode == NULL) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); + return -1; + } + +#ifdef CONFIG_IEEE80211N + if (!ieee80211n_allowed_ht40_channel_pair(iface)) + return -1; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211N */ + + if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates table."); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + + ret = hostapd_passive_scan(iface->bss[0], 0, + iface->conf->passive_scan_mode, + iface->conf->passive_scan_interval, + iface->conf->passive_scan_listen, + NULL, NULL); + if (ret) { + if (ret == -1) { + wpa_printf(MSG_DEBUG, "Passive scanning not " + "supported"); + } else { + wpa_printf(MSG_ERROR, "Could not set passive " + "scanning: %s", strerror(ret)); + } + ret = 0; + } + + return ret; +} + + +const char * hostapd_hw_mode_txt(int mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211A: + return "IEEE 802.11a"; + case HOSTAPD_MODE_IEEE80211B: + return "IEEE 802.11b"; + case HOSTAPD_MODE_IEEE80211G: + return "IEEE 802.11g"; + default: + return "UNKNOWN"; + } +} + + +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->chan == chan) + return ch->freq; + } + + return 0; +} + + +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->freq == freq) + return ch->chan; + } + + return 0; +} diff --git a/contrib/hostapd/hw_features.h b/hostapd/hw_features.h index 7e5d443432e89..7d43c89b66968 100644 --- a/contrib/hostapd/hw_features.h +++ b/hostapd/hw_features.h @@ -16,14 +16,16 @@ #ifndef HW_FEATURES_H #define HW_FEATURES_H -#define HOSTAPD_CHAN_W_SCAN 0x00000001 -#define HOSTAPD_CHAN_W_ACTIVE_SCAN 0x00000002 -#define HOSTAPD_CHAN_W_IBSS 0x00000004 +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 +#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_RADAR 0x00000008 struct hostapd_channel_data { short chan; /* channel number (IEEE 802.11) */ short freq; /* frequency in MHz */ int flag; /* flag for hostapd use (HOSTAPD_CHAN_*) */ + u8 max_tx_power; /* maximum transmit power in dBm */ }; #define HOSTAPD_RATE_ERP 0x00000001 @@ -45,15 +47,14 @@ struct hostapd_hw_modes { struct hostapd_channel_data *channels; int num_rates; struct hostapd_rate_data *rates; + u16 ht_capab; }; void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features); int hostapd_get_hw_features(struct hostapd_iface *iface); -int hostapd_select_hw_mode_start(struct hostapd_iface *iface, - hostapd_iface_cb cb); -int hostapd_select_hw_mode_stop(struct hostapd_iface *iface); +int hostapd_select_hw_mode(struct hostapd_iface *iface); const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); diff --git a/contrib/hostapd/iapp.c b/hostapd/iapp.c index 5e027fb06bac0..6d6dba8f7b9a3 100644 --- a/contrib/hostapd/iapp.c +++ b/hostapd/iapp.c @@ -13,7 +13,7 @@ * * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired * and IEEE has withdrawn it. In other words, it is likely better to look at - * using some other mechanism for AP-to-AP communication than extenting the + * using some other mechanism for AP-to-AP communication than extending the * implementation here. */ @@ -60,8 +60,8 @@ struct iapp_hdr { u8 version; u8 command; - u16 identifier; - u16 length; + be16 identifier; + be16 length; /* followed by length-6 octets of data */ } __attribute__ ((packed)); @@ -83,7 +83,7 @@ struct iapp_add_notify { u8 addr_len; /* ETH_ALEN */ u8 reserved; u8 mac_addr[ETH_ALEN]; - u16 seq_num; + be16 seq_num; } __attribute__ ((packed)); @@ -91,7 +91,7 @@ struct iapp_add_notify { struct iapp_layer2_update { u8 da[ETH_ALEN]; /* broadcast */ u8 sa[ETH_ALEN]; /* STA addr */ - u16 len; /* 6 */ + be16 len; /* 6 */ u8 dsap; /* null DSAP address */ u8 ssap; /* null SSAP address, CR=Response */ u8 control; @@ -197,11 +197,11 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) add = (struct iapp_add_notify *) (hdr + 1); add->addr_len = ETH_ALEN; add->reserved = 0; - memcpy(add->mac_addr, mac_addr, ETH_ALEN); + os_memcpy(add->mac_addr, mac_addr, ETH_ALEN); add->seq_num = host_to_be16(seq_num); - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = iapp->multicast.s_addr; addr.sin_port = htons(IAPP_UDP_PORT); @@ -221,8 +221,8 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ - memset(msg.da, 0xff, ETH_ALEN); - memcpy(msg.sa, addr, ETH_ALEN); + os_memset(msg.da, 0xff, ETH_ALEN); + os_memcpy(msg.sa, addr, ETH_ALEN); msg.len = host_to_be16(6); msg.dsap = 0; /* NULL DSAP address */ msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ @@ -238,6 +238,11 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) } +/** + * iapp_new_station - IAPP processing for a new STA + * @iapp: IAPP data + * @sta: The associated station + */ void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) { struct ieee80211_mgmt *assoc; @@ -306,6 +311,12 @@ static void iapp_process_add_notify(struct iapp_data *iapp, } +/** + * iapp_receive_udp - Process IAPP UDP frames + * @sock: File descriptor for the socket + * @eloop_ctx: IAPP data (struct iapp_data *) + * @sock_ctx: Not used + */ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) { struct iapp_data *iapp = eloop_ctx; @@ -386,7 +397,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) struct iapp_data *iapp; struct ip_mreqn mreq; - iapp = wpa_zalloc(sizeof(*iapp)); + iapp = os_zalloc(sizeof(*iapp)); if (iapp == NULL) return NULL; iapp->hapd = hapd; @@ -403,8 +414,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) return NULL; } - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { perror("ioctl(SIOCGIFINDEX)"); iapp_deinit(iapp); @@ -440,7 +451,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) } inet_aton(IAPP_MULTICAST, &iapp->multicast); - memset(&uaddr, 0, sizeof(uaddr)); + os_memset(&uaddr, 0, sizeof(uaddr)); uaddr.sin_family = AF_INET; uaddr.sin_port = htons(IAPP_UDP_PORT); if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, @@ -450,7 +461,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) return NULL; } - memset(&mreq, 0, sizeof(mreq)); + os_memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = iapp->multicast; mreq.imr_address.s_addr = INADDR_ANY; mreq.imr_ifindex = 0; @@ -468,7 +479,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) return NULL; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifindex; if (bind(iapp->packet_sock, (struct sockaddr *) &addr, @@ -504,7 +515,7 @@ void iapp_deinit(struct iapp_data *iapp) return; if (iapp->udp_sock >= 0) { - memset(&mreq, 0, sizeof(mreq)); + os_memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = iapp->multicast; mreq.imr_address.s_addr = INADDR_ANY; mreq.imr_ifindex = 0; @@ -520,21 +531,19 @@ void iapp_deinit(struct iapp_data *iapp) eloop_unregister_read_sock(iapp->packet_sock); close(iapp->packet_sock); } - free(iapp); + os_free(iapp); } int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, struct hostapd_bss_config *oldbss) { if (hapd->conf->ieee802_11f != oldbss->ieee802_11f || - strcmp(hapd->conf->iapp_iface, oldbss->iapp_iface) != 0) { - + os_strcmp(hapd->conf->iapp_iface, oldbss->iapp_iface) != 0) { iapp_deinit(hapd->iapp); hapd->iapp = NULL; if (hapd->conf->ieee802_11f) { hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface); - if (hapd->iapp == NULL) return -1; } diff --git a/contrib/hostapd/iapp.h b/hostapd/iapp.h index 86de59256043c..86de59256043c 100644 --- a/contrib/hostapd/iapp.h +++ b/hostapd/iapp.h diff --git a/contrib/hostapd/ieee802_11.c b/hostapd/ieee802_11.c index 817c30e2ae84d..8399d88e03c51 100644 --- a/contrib/hostapd/ieee802_11.c +++ b/hostapd/ieee802_11.c @@ -1,6 +1,7 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,11 +24,10 @@ #include "ieee802_11.h" #include "beacon.h" #include "hw_features.h" -#include "radius.h" -#include "radius_client.h" +#include "radius/radius.h" +#include "radius/radius_client.h" #include "ieee802_11_auth.h" #include "sta_info.h" -#include "eapol_sm.h" #include "rc4.h" #include "ieee802_1x.h" #include "wpa.h" @@ -35,7 +35,6 @@ #include "ap_list.h" #include "accounting.h" #include "driver.h" -#include "ieee802_11h.h" #include "mlme.h" @@ -101,6 +100,153 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) } +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_IEEE80211N + struct ieee80211_ht_capability *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n) + return eid; + + *pos++ = WLAN_EID_HT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_ht_capability *) pos; + os_memset(cap, 0, sizeof(*cap)); + SET_2BIT_U8(&cap->mac_ht_params_info, + MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET, + MAX_RX_AMPDU_FACTOR_64KB); + + cap->capabilities_info = host_to_le16(hapd->iconf->ht_capab); + + cap->supported_mcs_set[0] = 0xff; + cap->supported_mcs_set[1] = 0xff; + + pos += sizeof(*cap); + + return pos; +#else /* CONFIG_IEEE80211N */ + return eid; +#endif /* CONFIG_IEEE80211N */ +} + + +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_IEEE80211N + struct ieee80211_ht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n) + return eid; + + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_ht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + oper->control_chan = hapd->iconf->channel; + oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); + if (hapd->iconf->secondary_channel == 1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + if (hapd->iconf->secondary_channel == -1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + + pos += sizeof(*oper); + + return pos; +#else /* CONFIG_IEEE80211N */ + return eid; +#endif /* CONFIG_IEEE80211N */ +} + + +#ifdef CONFIG_IEEE80211N + +/* +op_mode +Set to 0 (HT pure) under the followign conditions + - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or + - all STAs in the BSS are 20 MHz HT in 20 MHz BSS +Set to 1 (HT non-member protection) if there may be non-HT STAs + in both the primary and the secondary channel +Set to 2 if only HT STAs are associated in BSS, + however and at least one 20 MHz HT STA is associated +Set to 3 (HT mixed mode) when one or more non-HT STAs are associated + (currently non-GF HT station is considered as non-HT STA also) +*/ +int hostapd_ht_operation_update(struct hostapd_iface *iface) +{ + u16 cur_op_mode, new_op_mode; + int op_mode_changes = 0; + + if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) + return 0; + + wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", + __func__, iface->ht_op_mode); + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) + && iface->num_sta_ht_no_gf) { + iface->ht_op_mode |= + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + iface->num_sta_ht_no_gf == 0) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht || iface->olbc_ht)) { + iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } + + /* Note: currently we switch to the MIXED op mode if HT non-greenfield + * station is associated. Probably it's a theoretical case, since + * it looks like all known HT STAs support greenfield. + */ + new_op_mode = 0; + if (iface->num_sta_no_ht || + (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + new_op_mode = OP_MODE_MIXED; + else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) + && iface->num_sta_ht_20mhz) + new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + else if (iface->olbc_ht) + new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + else + new_op_mode = OP_MODE_PURE; + + cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + if (cur_op_mode != new_op_mode) { + iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + iface->ht_op_mode |= new_op_mode; + op_mode_changes++; + } + + wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", + __func__, iface->ht_op_mode, op_mode_changes); + + return op_mode_changes; +} + +#endif /* CONFIG_IEEE80211N */ + + u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe) { @@ -138,238 +284,77 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; - if (hapd->iface && hapd->iface->current_mode && + if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - if (hapd->iface->dfs_enable) - capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - return capab; } -#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) - * 00:50:F2 */ - -static int ieee802_11_parse_vendor_specific(struct hostapd_data *hapd, - u8 *pos, size_t elen, - struct ieee802_11_elems *elems, - int show_errors) -{ - unsigned int oui; - - /* first 3 bytes in vendor specific information element are the IEEE - * OUI of the vendor. The following byte is used a vendor specific - * sub-type. */ - if (elen < 4) { - if (show_errors) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, "short vendor " - "specific information element ignored " - "(len=%lu)\n", (unsigned long) elen); - } - return -1; - } - - oui = (pos[0] << 16) | (pos[1] << 8) | pos[2]; - switch (oui) { - case OUI_MICROSOFT: - /* Microsoft/Wi-Fi information elements are further typed and - * subtyped */ - switch (pos[3]) { - case 1: - /* Microsoft OUI (00:50:F2) with OUI Type 1: - * real WPA information element */ - elems->wpa_ie = pos; - elems->wpa_ie_len = elen; - break; - case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ - if (elen < 5) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, - "short WME information element " - "ignored (len=%lu)\n", - (unsigned long) elen); - return -1; - } - switch (pos[4]) { - case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: - case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: - elems->wme = pos; - elems->wme_len = elen; - break; - case WME_OUI_SUBTYPE_TSPEC_ELEMENT: - elems->wme_tspec = pos; - elems->wme_tspec_len = elen; - break; - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, - "unknown WME information element" - " ignored (subtype=%d " - "len=%lu)\n", - pos[4], (unsigned long) elen); - return -1; - } - break; - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, - "Unknown Microsoft information element " - "ignored (type=%d len=%lu)\n", - pos[3], (unsigned long) elen); - return -1; - } - break; - - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, - "unknown vendor specific information element " - "ignored (vendor OUI %02x:%02x:%02x len=%lu)\n", - pos[0], pos[1], pos[2], (unsigned long) elen); - return -1; - } - - return 0; -} - - -ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, - size_t len, - struct ieee802_11_elems *elems, - int show_errors) +#ifdef CONFIG_IEEE80211W +static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) { - size_t left = len; - u8 *pos = start; - int unknown = 0; - - memset(elems, 0, sizeof(*elems)); - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.11 element parse " - "failed (id=%d elen=%d " - "left=%lu)\n", - id, elen, (unsigned long) left); - wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); - } - return ParseFailed; - } - - switch (id) { - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; - case WLAN_EID_DS_PARAMS: - elems->ds_params = pos; - elems->ds_params_len = elen; - break; - case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; - case WLAN_EID_TIM: - elems->tim = pos; - elems->tim_len = elen; - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; - break; - case WLAN_EID_CHALLENGE: - elems->challenge = pos; - elems->challenge_len = elen; - break; - case WLAN_EID_ERP_INFO: - elems->erp_info = pos; - elems->erp_info_len = elen; - break; - case WLAN_EID_EXT_SUPP_RATES: - elems->ext_supp_rates = pos; - elems->ext_supp_rates_len = elen; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (ieee802_11_parse_vendor_specific(hapd, pos, elen, - elems, - show_errors)) - unknown++; - break; - case WLAN_EID_RSN: - elems->rsn_ie = pos; - elems->rsn_ie_len = elen; - break; - case WLAN_EID_PWR_CAPABILITY: - elems->power_cap = pos; - elems->power_cap_len = elen; - break; - case WLAN_EID_SUPPORTED_CHANNELS: - elems->supp_channels = pos; - elems->supp_channels_len = elen; - break; - default: - unknown++; - if (!show_errors) - break; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, - "IEEE 802.11 element parse ignored " - "unknown element (id=%d elen=%d)\n", - id, elen); - break; - } - - left -= elen; - pos += elen; - } - - if (left) - return ParseFailed; + u8 *pos = eid; + u32 timeout, tu; + struct os_time now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; - return unknown ? ParseUnknown : ParseOK; + return pos; } +#endif /* CONFIG_IEEE80211W */ -void ieee802_11_print_ssid(const u8 *ssid, u8 len) +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) { int i; + if (len > HOSTAPD_MAX_SSID_LEN) + len = HOSTAPD_MAX_SSID_LEN; for (i = 0; i < len; i++) { if (ssid[i] >= 32 && ssid[i] < 127) - printf("%c", ssid[i]); + buf[i] = ssid[i]; else - printf("<%02x>", ssid[i]); + buf[i] = '.'; } + buf[len] = '\0'; } +/** + * ieee802_11_send_deauth - Send Deauthentication frame + * @hapd: hostapd BSS data + * @addr: Address of the destination STA + * @reason: Reason code for Deauthentication + */ void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason) { struct ieee80211_mgmt mgmt; - char buf[30]; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticate - reason %d", reason); - snprintf(buf, sizeof(buf), "SEND-DEAUTHENTICATE %d", reason); - memset(&mgmt, 0, sizeof(mgmt)); + os_memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); - memcpy(mgmt.da, addr, ETH_ALEN); - memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), 0) < 0) @@ -377,88 +362,6 @@ void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason) } -static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct ieee80211_mgmt mgmt; - - if (hapd->assoc_ap_state == WAIT_BEACON) - hapd->assoc_ap_state = AUTHENTICATE; - if (hapd->assoc_ap_state != AUTHENTICATE) - return; - - printf("Authenticate with AP " MACSTR " SSID=", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, - hapd->assoc_ap_ssid_len); - printf(" (as station)\n"); - - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - /* Request TX callback */ - mgmt.frame_control |= host_to_le16(BIT(1)); - memcpy(mgmt.da, hapd->conf->assoc_ap_addr, ETH_ALEN); - memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); - mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); - mgmt.u.auth.auth_transaction = host_to_le16(1); - mgmt.u.auth.status_code = host_to_le16(0); - if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.auth), 0) < 0) - perror("ieee802_11_sta_authenticate: send"); - - /* Try to authenticate again, if this attempt fails or times out. */ - eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, hapd, NULL); -} - - -static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - u8 buf[256]; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - u8 *p; - - if (hapd->assoc_ap_state == AUTHENTICATE) - hapd->assoc_ap_state = ASSOCIATE; - if (hapd->assoc_ap_state != ASSOCIATE) - return; - - printf("Associate with AP " MACSTR " SSID=", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, - hapd->assoc_ap_ssid_len); - printf(" (as station)\n"); - - memset(mgmt, 0, sizeof(*mgmt)); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ASSOC_REQ); - /* Request TX callback */ - mgmt->frame_control |= host_to_le16(BIT(1)); - memcpy(mgmt->da, hapd->conf->assoc_ap_addr, ETH_ALEN); - memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); - memcpy(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); - mgmt->u.assoc_req.capab_info = host_to_le16(0); - mgmt->u.assoc_req.listen_interval = host_to_le16(1); - p = &mgmt->u.assoc_req.variable[0]; - - *p++ = WLAN_EID_SSID; - *p++ = hapd->assoc_ap_ssid_len; - memcpy(p, hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len); - p += hapd->assoc_ap_ssid_len; - - p = hostapd_eid_supp_rates(hapd, p); - p = hostapd_eid_ext_supp_rates(hapd, p); - - if (hostapd_send_mgmt_frame(hapd, mgmt, p - (u8 *) mgmt, 0) < 0) - perror("ieee802_11_sta_associate: send"); - - /* Try to authenticate again, if this attempt fails or times out. */ - eloop_register_timeout(5, 0, ieee802_11_sta_associate, hapd, NULL); -} - - static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, u8 *challenge, int iswep) { @@ -473,14 +376,14 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u8 key[8]; time_t now; int r; - sta->challenge = wpa_zalloc(WLAN_AUTH_CHALLENGE_LEN); + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; now = time(NULL); r = random(); - memcpy(key, &now, 4); - memcpy(key + 4, &r, 4); + os_memcpy(key, &now, 4); + os_memcpy(key + 4, &r, 4); rc4(sta->challenge, WLAN_AUTH_CHALLENGE_LEN, key, sizeof(key)); } @@ -492,7 +395,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, /* Transaction 3 */ if (!iswep || !sta->challenge || !challenge || - memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { + os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "shared key authentication - invalid " @@ -510,7 +413,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); #endif - free(sta->challenge); + os_free(sta->challenge); sta->challenge = NULL; return 0; @@ -518,47 +421,68 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, static void send_auth_reply(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, + const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, - u8 *challenge) + const u8 *ies, size_t ies_len) { - u8 buf[IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + - WLAN_AUTH_CHALLENGE_LEN]; struct ieee80211_mgmt *reply; + u8 *buf; size_t rlen; - memset(buf, 0, sizeof(buf)); + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; + buf = os_zalloc(rlen); + if (buf == NULL) + return; + reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - /* Request TX callback */ - reply->frame_control |= host_to_le16(BIT(1)); - memcpy(reply->da, mgmt->sa, ETH_ALEN); - memcpy(reply->sa, hapd->own_addr, ETH_ALEN); - memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); + WLAN_FC_STYPE_AUTH); + os_memcpy(reply->da, dst, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, bssid, ETH_ALEN); reply->u.auth.auth_alg = host_to_le16(auth_alg); reply->u.auth.auth_transaction = host_to_le16(auth_transaction); reply->u.auth.status_code = host_to_le16(resp); - rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth); - if (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 2 && - challenge) { - u8 *p = reply->u.auth.variable; - *p++ = WLAN_EID_CHALLENGE; - *p++ = WLAN_AUTH_CHALLENGE_LEN; - memcpy(p, challenge, WLAN_AUTH_CHALLENGE_LEN); - rlen += 2 + WLAN_AUTH_CHALLENGE_LEN; - } else - challenge = NULL; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "authentication reply: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d resp=%d%s\n", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, - resp, challenge ? " challenge" : ""); + if (ies && ies_len) + os_memcpy(reply->u.auth.variable, ies, ies_len); + + wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + MAC2STR(dst), auth_alg, auth_transaction, + resp, (unsigned long) ies_len); if (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0) perror("send_auth_reply: send"); + + os_free(buf); +} + + +#ifdef CONFIG_IEEE80211R +static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, + status, ies, ies_len); + + if (status != WLAN_STATUS_SUCCESS) + return; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + mlme_authenticate_indication(hapd, sta); } +#endif /* CONFIG_IEEE80211R */ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, @@ -572,6 +496,8 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { printf("handle_auth - too short payload (len=%lu)\n", @@ -590,37 +516,26 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) challenge = &mgmt->u.auth.variable[2]; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s\n", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, - status_code, !!(fc & WLAN_FC_ISWEP), - challenge ? " challenge" : ""); - - if (hapd->assoc_ap_state == AUTHENTICATE && auth_transaction == 2 && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0 && - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - if (status_code != 0) { - printf("Authentication (as station) with AP " - MACSTR " failed (status_code=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), - status_code); - return; - } - printf("Authenticated (as station) with AP " MACSTR "\n", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_sta_associate(hapd, NULL); - return; - } + wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d status_code=%d wep=%d%s", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + status_code, !!(fc & WLAN_FC_ISWEP), + challenge ? " challenge" : ""); if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; } - if (!(((hapd->conf->auth_algs & HOSTAPD_AUTH_OPEN) && + if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || - ((hapd->conf->auth_algs & HOSTAPD_AUTH_SHARED_KEY) && +#ifdef CONFIG_IEEE80211R + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && + auth_alg == WLAN_AUTH_FT) || +#endif /* CONFIG_IEEE80211R */ + ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { printf("Unsupported authentication algorithm (%d)\n", auth_alg); @@ -636,7 +551,7 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, goto fail; } - if (memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { + if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { printf("Station " MACSTR " not allowed to authenticate.\n", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -653,9 +568,9 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, goto fail; } if (res == HOSTAPD_ACL_PENDING) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Authentication frame " - "from " MACSTR " waiting for an external " - "authentication\n", MAC2STR(mgmt->sa)); + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + MAC2STR(mgmt->sa)); /* Authentication code will re-send the authentication frame * after it has received (and cached) information from the * external source. */ @@ -714,12 +629,39 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, fc & WLAN_FC_ISWEP); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); + if (sta->challenge && auth_transaction == 1) { + resp_ies[0] = WLAN_EID_CHALLENGE; + resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; + os_memcpy(resp_ies + 2, sta->challenge, + WLAN_AUTH_CHALLENGE_LEN); + resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; + } break; +#ifdef CONFIG_IEEE80211R + case WLAN_AUTH_FT: + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, + auth_transaction, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth), + handle_auth_ft_finish, hapd); + /* handle_auth_ft_finish() callback will complete auth. */ + return; +#endif /* CONFIG_IEEE80211R */ } fail: - send_auth_reply(hapd, mgmt, auth_alg, auth_transaction + 1, resp, - sta ? sta->challenge : NULL); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, resp_ies_len); } @@ -733,6 +675,8 @@ static void handle_assoc(struct hostapd_data *hapd, int send_deauth = 0, send_len, left, i; struct sta_info *sta; struct ieee802_11_elems elems; + u8 buf[sizeof(struct ieee80211_mgmt) + 512]; + struct ieee80211_mgmt *reply; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -745,27 +689,33 @@ static void handle_assoc(struct hostapd_data *hapd, capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "reassociation request: STA=" MACSTR - " capab_info=0x%02x " - "listen_interval=%d current_ap=" MACSTR "\n", - MAC2STR(mgmt->sa), capab_info, listen_interval, - MAC2STR(mgmt->u.reassoc_req.current_ap)); + wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d current_ap=" + MACSTR, + MAC2STR(mgmt->sa), capab_info, listen_interval, + MAC2STR(mgmt->u.reassoc_req.current_ap)); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "association request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d\n", - MAC2STR(mgmt->sa), capab_info, listen_interval); + wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d", + MAC2STR(mgmt->sa), capab_info, listen_interval); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } sta = ap_get_sta(hapd, mgmt->sa); +#ifdef CONFIG_IEEE80211R + if (sta && sta->auth_alg == WLAN_AUTH_FT && + (sta->flags & WLAN_STA_AUTH) == 0) { + wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " + "prior to authentication since it is using " + "over-the-DS FT", MAC2STR(mgmt->sa)); + } else +#endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { printf("STA " MACSTR " trying to associate before " "authentication\n", MAC2STR(mgmt->sa)); @@ -783,16 +733,22 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - if (reassoc) { - memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, - ETH_ALEN); + if (listen_interval > hapd->conf->max_listen_interval) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Too large Listen Interval (%d)", + listen_interval); + resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; + goto fail; } sta->capability = capab_info; + sta->listen_interval = listen_interval; - /* followed by SSID and Supported rates */ - if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed - || !elems.ssid) { + /* followed by SSID and Supported rates; and HT capabilities if 802.11n + * is used */ + if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed || + !elems.ssid) { printf("STA " MACSTR " sent invalid association request\n", MAC2STR(sta->addr)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -800,11 +756,12 @@ static void handle_assoc(struct hostapd_data *hapd, } if (elems.ssid_len != hapd->conf->ssid.ssid_len || - memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0) { + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0) + { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); printf("Station " MACSTR " tried to associate with " - "unknown SSID '", MAC2STR(sta->addr)); - ieee802_11_print_ssid(elems.ssid, elems.ssid_len); - printf("'\n"); + "unknown SSID '%s'\n", MAC2STR(sta->addr), ssid_txt); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -838,8 +795,9 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - memcpy(sta->supported_rates, elems.supp_rates, elems.supp_rates_len); + os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + os_memcpy(sta->supported_rates, elems.supp_rates, + elems.supp_rates_len); sta->supported_rates_len = elems.supp_rates_len; if (elems.ext_supp_rates) { @@ -855,15 +813,32 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - memcpy(sta->supported_rates + elems.supp_rates_len, - elems.ext_supp_rates, elems.ext_supp_rates_len); + os_memcpy(sta->supported_rates + elems.supp_rates_len, + elems.ext_supp_rates, elems.ext_supp_rates_len); sta->supported_rates_len += elems.ext_supp_rates_len; } - if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && elems.rsn_ie) { +#ifdef CONFIG_IEEE80211N + /* save HT capabilities in the sta object */ + os_memset(&sta->ht_capabilities, 0, sizeof(sta->ht_capabilities)); + if (elems.ht_capabilities && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_ht_capability)) { + sta->flags |= WLAN_STA_HT; + sta->ht_capabilities.id = WLAN_EID_HT_CAP; + sta->ht_capabilities.length = + sizeof(struct ieee80211_ht_capability); + os_memcpy(&sta->ht_capabilities.data, + elems.ht_capabilities, + sizeof(struct ieee80211_ht_capability)); + } else + sta->flags &= ~WLAN_STA_HT; +#endif /* CONFIG_IEEE80211N */ + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; - } else if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) && + } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && elems.wpa_ie) { wpa_ie = elems.wpa_ie; wpa_ie_len = elems.wpa_ie_len; @@ -871,6 +846,25 @@ static void handle_assoc(struct hostapd_data *hapd, wpa_ie = NULL; wpa_ie_len = 0; } +#ifdef CONFIG_WPS + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + if (hapd->conf->wps_state && wpa_ie == NULL) { + if (elems.wps_ie) { + wpa_printf(MSG_DEBUG, "STA included WPS IE in " + "(Re)Association Request - assume WPS is " + "used"); + sta->flags |= WLAN_STA_WPS; + wpabuf_free(sta->wps_ie); + sta->wps_ie = wpabuf_alloc_copy(elems.wps_ie + 4, + elems.wps_ie_len - 4); + } else { + wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE " + "in (Re)Association Request - possible WPS " + "use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + } + } else +#endif /* CONFIG_WPS */ if (hapd->conf->wpa && wpa_ie == NULL) { printf("STA " MACSTR ": No WPA/RSN IE in association " "request\n", MAC2STR(sta->addr)); @@ -878,7 +872,7 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - if (hapd->conf->wpa) { + if (hapd->conf->wpa && wpa_ie) { int res; wpa_ie -= 2; wpa_ie_len += 2; @@ -891,7 +885,8 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - wpa_ie, wpa_ie_len); + wpa_ie, wpa_ie_len, + elems.mdie, elems.mdie_len); if (res == WPA_INVALID_GROUP) resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; else if (res == WPA_INVALID_PAIRWISE) @@ -902,29 +897,70 @@ static void handle_assoc(struct hostapd_data *hapd, resp = WLAN_STATUS_UNSPECIFIED_FAILURE; #ifdef CONFIG_IEEE80211W else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; /* FIX */ + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; /* FIX */ + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; #endif /* CONFIG_IEEE80211W */ + else if (res == WPA_INVALID_MDIE) + resp = WLAN_STATUS_INVALID_MDIE; else if (res != WPA_IE_OK) resp = WLAN_STATUS_INVALID_IE; if (resp != WLAN_STATUS_SUCCESS) goto fail; - } +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + + resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + goto fail; + } - if (hapd->iface->dfs_enable && - hapd->iconf->ieee80211h == SPECT_STRICT_BINDING) { - if (hostapd_check_power_cap(hapd, elems.power_cap, - elems.power_cap_len)) { - resp = WLAN_STATUS_PWR_CAPABILITY_NOT_VALID; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Power capabilities of the station not " - "acceptable"); + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " + "to use association (not " + "re-association) with FT auth_alg", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + resp = wpa_ft_validate_reassoc(sta->wpa_sm, pos, left); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211N + if ((sta->flags & WLAN_STA_HT) && + wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "HT: " MACSTR " tried to " + "use TKIP with HT association", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; goto fail; } - } +#endif /* CONFIG_IEEE80211N */ + } else + wpa_auth_sta_no_wpa(sta->wpa_sm); if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; @@ -965,10 +1001,52 @@ static void handle_assoc(struct hostapd_data *hapd, ieee802_11_set_beacons(hapd->iface); } +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) { + u16 ht_capab = le_to_host16( + sta->ht_capabilities.data.capabilities_info); + wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities " + "Info: 0x%04x", MAC2STR(sta->addr), ht_capab); + if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { + if (!sta->no_ht_gf_set) { + sta->no_ht_gf_set = 1; + hapd->iface->num_sta_ht_no_gf++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no " + "greenfield, num of non-gf stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_no_gf); + } + if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { + if (!sta->ht_20mhz_set) { + sta->ht_20mhz_set = 1; + hapd->iface->num_sta_ht_20mhz++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, " + "num of 20MHz HT STAs %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_20mhz); + } + } else { + if (!sta->no_ht_set) { + sta->no_ht_set = 1; + hapd->iface->num_sta_no_ht++; + } + if (hapd->iconf->ieee80211n) { + wpa_printf(MSG_DEBUG, "%s STA " MACSTR + " - no HT, num of non-HT stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_no_ht); + } + } + + if (hostapd_ht_operation_update(hapd->iface) > 0) + ieee802_11_set_beacons(hapd->iface); +#endif /* CONFIG_IEEE80211N */ + /* get a unique AID */ if (sta->aid > 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " old AID %d\n", sta->aid); + wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); } else { for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) if (hapd->sta_aid[sta->aid - 1] == NULL) @@ -976,12 +1054,11 @@ static void handle_assoc(struct hostapd_data *hapd, if (sta->aid > MAX_AID_TABLE_SIZE) { sta->aid = 0; resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - printf(" no room for more AIDs\n"); + wpa_printf(MSG_ERROR, " no room for more AIDs"); goto fail; } else { hapd->sta_aid[sta->aid - 1] = sta; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " new AID %d\n", sta->aid); + wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); } } @@ -991,106 +1068,88 @@ static void handle_assoc(struct hostapd_data *hapd, /* Station will be marked associated, after it acknowledges AssocResp */ +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { + wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " + "SA Query procedure", reassoc ? "re" : ""); + /* TODO: Send a protected Disassociate frame to the STA using + * the old key and Reason Code "Previous Authentication no + * longer valid". Make sure this is only sent protected since + * unprotected frame would be received by the STA that is now + * trying to associate. + */ + } +#endif /* CONFIG_IEEE80211W */ + + if (reassoc) { + os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, + ETH_ALEN); + } + if (sta->last_assoc_req) - free(sta->last_assoc_req); - sta->last_assoc_req = (struct ieee80211_mgmt *) malloc(len); + os_free(sta->last_assoc_req); + sta->last_assoc_req = os_malloc(len); if (sta->last_assoc_req) - memcpy(sta->last_assoc_req, mgmt, len); + os_memcpy(sta->last_assoc_req, mgmt, len); /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; fail: - - /* use the queued buffer for transmission because it is large enough - * and not needed anymore */ - mgmt->frame_control = + os_memset(buf, 0, sizeof(buf)); + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (send_deauth ? WLAN_FC_STYPE_DEAUTH : (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP))); - memcpy(mgmt->da, mgmt->sa, ETH_ALEN); - memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); - /* Addr3 = BSSID - already set */ + os_memcpy(reply->da, mgmt->sa, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); send_len = IEEE80211_HDRLEN; if (send_deauth) { - send_len += sizeof(mgmt->u.deauth); - mgmt->u.deauth.reason_code = host_to_le16(resp); + send_len += sizeof(reply->u.deauth); + reply->u.deauth.reason_code = host_to_le16(resp); } else { u8 *p; - send_len += sizeof(mgmt->u.assoc_resp); - mgmt->u.assoc_resp.capab_info = + send_len += sizeof(reply->u.assoc_resp); + reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); - mgmt->u.assoc_resp.status_code = host_to_le16(resp); - mgmt->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) - | BIT(14) | BIT(15)); + reply->u.assoc_resp.status_code = host_to_le16(resp); + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) + | BIT(14) | BIT(15)); /* Supported rates */ - p = hostapd_eid_supp_rates(hapd, mgmt->u.assoc_resp.variable); + p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); if (sta->flags & WLAN_STA_WME) p = hostapd_eid_wme(hapd, p); - send_len += p - mgmt->u.assoc_resp.variable; - - /* Request TX callback */ - mgmt->frame_control |= host_to_le16(BIT(1)); - } - if (hostapd_send_mgmt_frame(hapd, mgmt, send_len, 0) < 0) - perror("handle_assoc: send"); -} + p = hostapd_eid_ht_capabilities_info(hapd, p); + p = hostapd_eid_ht_operation(hapd, p); +#ifdef CONFIG_IEEE80211R + if (resp == WLAN_STATUS_SUCCESS) { + /* IEEE 802.11r: Mobility Domain Information, Fast BSS + * Transition Information, RSN */ + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, + buf + sizeof(buf) - p, + sta->auth_alg); + } +#endif /* CONFIG_IEEE80211R */ -static void handle_assoc_resp(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len) -{ - u16 status_code, aid; - - if (hapd->assoc_ap_state != ASSOCIATE) { - printf("Unexpected association response received from " MACSTR - "\n", MAC2STR(mgmt->sa)); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) { - printf("handle_assoc_resp - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - if (memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0 || - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0) { - printf("Received association response from unexpected address " - "(SA=" MACSTR " BSSID=" MACSTR "\n", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - status_code = le_to_host16(mgmt->u.assoc_resp.status_code); - aid = le_to_host16(mgmt->u.assoc_resp.aid); - aid &= ~(BIT(14) | BIT(15)); +#ifdef CONFIG_IEEE80211W + if (resp == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); +#endif /* CONFIG_IEEE80211W */ - if (status_code != 0) { - printf("Association (as station) with AP " MACSTR " failed " - "(status_code=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), status_code); - /* Try to authenticate again */ - hapd->assoc_ap_state = AUTHENTICATE; - eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, - hapd, NULL); + send_len += p - reply->u.assoc_resp.variable; } - printf("Associated (as station) with AP " MACSTR " (aid=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), aid); - hapd->assoc_ap_aid = aid; - hapd->assoc_ap_state = ASSOCIATED; - - if (hostapd_set_assoc_ap(hapd, hapd->conf->assoc_ap_addr)) { - printf("Could not set associated AP address to kernel " - "driver.\n"); - } + if (hostapd_send_mgmt_frame(hapd, reply, send_len, 0) < 0) + perror("handle_assoc: send"); } @@ -1105,23 +1164,9 @@ static void handle_disassoc(struct hostapd_data *hapd, return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "disassocation: STA=" MACSTR " reason_code=%d\n", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.disassoc.reason_code)); - - if (hapd->assoc_ap_state != DO_NOT_ASSOC && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - printf("Assoc AP " MACSTR " sent disassociation " - "(reason_code=%d) - try to authenticate\n", - MAC2STR(hapd->conf->assoc_ap_addr), - le_to_host16(mgmt->u.disassoc.reason_code)); - hapd->assoc_ap_state = AUTHENTICATE; - ieee802_11_sta_authenticate(hapd, NULL); - eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, - hapd, NULL); - return; - } + wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.disassoc.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { @@ -1166,22 +1211,10 @@ static void handle_deauth(struct hostapd_data *hapd, return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "deauthentication: STA=" MACSTR " reason_code=%d\n", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.deauth.reason_code)); - - if (hapd->assoc_ap_state != DO_NOT_ASSOC && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - printf("Assoc AP " MACSTR " sent deauthentication " - "(reason_code=%d) - try to authenticate\n", - MAC2STR(hapd->conf->assoc_ap_addr), - le_to_host16(mgmt->u.deauth.reason_code)); - hapd->assoc_ap_state = AUTHENTICATE; - eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, - hapd, NULL); - return; - } + wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR + " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.deauth.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { @@ -1214,37 +1247,90 @@ static void handle_beacon(struct hostapd_data *hapd, return; } - (void) ieee802_11_parse_elems(hapd, mgmt->u.beacon.variable, + (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, len - (IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)), &elems, 0); - if (hapd->assoc_ap_state == WAIT_BEACON && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - if (elems.ssid && elems.ssid_len <= 32) { - memcpy(hapd->assoc_ap_ssid, elems.ssid, - elems.ssid_len); - hapd->assoc_ap_ssid[elems.ssid_len] = '\0'; - hapd->assoc_ap_ssid_len = elems.ssid_len; - } - ieee802_11_sta_authenticate(hapd, NULL); + ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); +} + + +#ifdef CONFIG_IEEE80211W + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + perror("ieee802_11_send_sa_query_req: send"); +} + + +static void hostapd_sa_query_action(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + u8 *end; + int i; + + end = mgmt->u.action.u.sa_query_resp.trans_id + + WLAN_SA_QUERY_TR_ID_LEN; + if (((u8 *) mgmt) + len < end) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " + "frame (len=%lu)", (unsigned long) len); + return; } - ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); + if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", mgmt->u.action.u.sa_query_resp.action); + return; + } + + /* MLME-SAQuery.confirm */ - if (!HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_EXCESSIVE)) + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); return; + } - printf("Beacon from " MACSTR, MAC2STR(mgmt->sa)); - if (elems.ssid) { - printf(" SSID='"); - ieee802_11_print_ssid(elems.ssid, elems.ssid_len); - printf("'"); + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; } - if (elems.ds_params && elems.ds_params_len == 1) - printf(" CHAN=%d", elems.ds_params[0]); - printf("\n"); + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); } +#endif /* CONFIG_IEEE80211W */ static void handle_action(struct hostapd_data *hapd, @@ -1259,14 +1345,40 @@ static void handle_action(struct hostapd_data *hapd, } switch (mgmt->u.action.category) { - case WME_ACTION_CATEGORY: +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + { + struct sta_info *sta; + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " + "frame from unassociated STA " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + len - IEEE80211_HDRLEN)) + break; + + return; + } +#endif /* CONFIG_IEEE80211R */ + case WLAN_ACTION_WMM: hostapd_wme_action(hapd, mgmt, len); return; +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + hostapd_sa_query_action(hapd, mgmt, len); + return; +#endif /* CONFIG_IEEE80211W */ } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "handle_action - unknown action category %d", + "handle_action - unknown action category %d or invalid " + "frame", mgmt->u.action.category); if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && !(mgmt->sa[0] & 0x01)) { @@ -1294,6 +1406,7 @@ static void handle_action(struct hostapd_data *hapd, * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @stype: management frame subtype from frame control field + * @fi: meta data about received frame (signal level, etc.) * * Process all incoming IEEE 802.11 management frames. This will be called for * each frame received from the kernel driver through wlan#ap interface. In @@ -1307,7 +1420,6 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, int broadcast; if (stype == WLAN_FC_STYPE_BEACON) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, "mgmt::beacon\n"); handle_beacon(hapd, mgmt, len, fi); return; } @@ -1319,9 +1431,8 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; - if (!broadcast && memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0 && - (hapd->assoc_ap_state == DO_NOT_ASSOC || - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0)) { + if (!broadcast && + os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { printf("MGMT: BSSID=" MACSTR " not our address\n", MAC2STR(mgmt->bssid)); return; @@ -1329,12 +1440,11 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, if (stype == WLAN_FC_STYPE_PROBE_REQ) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MSGDUMPS, "mgmt::probe_req\n"); handle_probe_req(hapd, mgmt, len); return; } - if (memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", @@ -1344,31 +1454,27 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, switch (stype) { case WLAN_FC_STYPE_AUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth\n"); + wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len); break; case WLAN_FC_STYPE_ASSOC_REQ: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_req\n"); + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0); break; - case WLAN_FC_STYPE_ASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_resp\n"); - handle_assoc_resp(hapd, mgmt, len); - break; case WLAN_FC_STYPE_REASSOC_REQ: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::reassoc_req\n"); + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1); break; case WLAN_FC_STYPE_DISASSOC: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::disassoc\n"); + wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); break; case WLAN_FC_STYPE_DEAUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n"); + wpa_printf(MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); break; case WLAN_FC_STYPE_ACTION: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::action\n"); + wpa_printf(MSG_DEBUG, "mgmt::action"); handle_action(hapd, mgmt, len); break; default: @@ -1428,6 +1534,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, u16 status; struct sta_info *sta; int new_assoc = 1; + struct ht_cap_ie *ht_cap = NULL; if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, @@ -1461,13 +1568,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, /* Stop previous accounting session, if one is started, and allocate * new session id for the new session. */ accounting_sta_stop(hapd, sta); - accounting_sta_get_id(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, - "associated (aid %d, accounting session %08X-%08X)", - sta->aid, sta->acct_session_id_hi, - sta->acct_session_id_lo); + "associated (aid %d)", + sta->aid); if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; @@ -1478,9 +1583,20 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else mlme_associate_indication(hapd, sta); +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + ht_cap = &sta->ht_capabilities; +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_IEEE80211W + sta->sa_query_timed_out = 0; +#endif /* CONFIG_IEEE80211W */ + if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, - sta->supported_rates_len, 0)) { + sta->supported_rates_len, 0, sta->listen_interval, + ht_cap)) + { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); @@ -1498,14 +1614,17 @@ static void handle_assoc_cb(struct hostapd_data *hapd, ap_sta_bind_vlan(hapd, sta, 0); } if (sta->flags & WLAN_STA_SHORT_PREAMBLE) { - hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, WLAN_STA_SHORT_PREAMBLE, ~0); } else { - hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, 0, ~WLAN_STA_SHORT_PREAMBLE); } - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + if (sta->auth_alg == WLAN_AUTH_FT) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hostapd_new_assoc_sta(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); @@ -1513,12 +1632,21 @@ static void handle_assoc_cb(struct hostapd_data *hapd, fail: /* Copy of the association request is not needed anymore */ if (sta->last_assoc_req) { - free(sta->last_assoc_req); + os_free(sta->last_assoc_req); sta->last_assoc_req = NULL; } } +/** + * ieee802_11_mgmt_cb - Process management frame TX status callback + * @hapd: hostapd BSS data structure (the BSS from which the management frame + * was sent from) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @stype: management frame subtype from frame control field + * @ok: Whether the frame was ACK'ed + */ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, int ok) { @@ -1526,21 +1654,25 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, switch (stype) { case WLAN_FC_STYPE_AUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth cb\n"); + wpa_printf(MSG_DEBUG, "mgmt::auth cb"); handle_auth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "mgmt::assoc_resp cb\n"); + wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 0, ok); break; case WLAN_FC_STYPE_REASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "mgmt::reassoc_resp cb\n"); + wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::proberesp cb\n"); + wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); + break; + case WLAN_FC_STYPE_DEAUTH: + /* ignore */ + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action cb"); break; default: printf("unknown mgmt cb frame subtype %d\n", stype); @@ -1599,10 +1731,10 @@ void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, "received frame"); mlme_michaelmicfailure_indication(hapd, addr); } else { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "MLME-MICHAELMICFAILURE.indication " - "for not associated STA (" MACSTR - ") ignored\n", MAC2STR(addr)); + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "for not associated STA (" MACSTR + ") ignored", MAC2STR(addr)); return; } } diff --git a/hostapd/ieee802_11.h b/hostapd/ieee802_11.h new file mode 100644 index 0000000000000..ca8ef93c090eb --- /dev/null +++ b/hostapd/ieee802_11.h @@ -0,0 +1,56 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_H +#define IEEE802_11_H + +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + +struct hostapd_frame_info { + u32 phytype; + u32 channel; + u32 datarate; + u32 ssi_signal; + + unsigned int passive_scan:1; +}; + +struct hostapd_iface; +struct hostapd_data; +struct sta_info; + +void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason); +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, struct hostapd_frame_info *fi); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, int ok); +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); +void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, + int local); +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe); +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +int hostapd_ht_operation_update(struct hostapd_iface *iface); +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id); + +#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd/ieee802_11_auth.c b/hostapd/ieee802_11_auth.c index 75d0494e3c0c4..9aba1fe854bd9 100644 --- a/contrib/hostapd/ieee802_11_auth.c +++ b/hostapd/ieee802_11_auth.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,6 +10,11 @@ * license. * * See README and COPYING for more details. + * + * Access control list for IEEE 802.11 authentication can uses statically + * configured ACL from configuration files or an external RADIUS server. + * Results from external RADIUS queries are cached to allow faster + * authentication frame processing. */ #include "includes.h" @@ -19,8 +24,8 @@ #include "hostapd.h" #include "ieee802_11.h" #include "ieee802_11_auth.h" -#include "radius.h" -#include "radius_client.h" +#include "radius/radius.h" +#include "radius/radius_client.h" #include "eloop.h" #include "driver.h" @@ -55,7 +60,7 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; - free(prev); + os_free(prev); } } @@ -71,14 +76,16 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, entry = hapd->acl_cache; while (entry) { - if (memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) return -1; /* entry has expired */ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) if (session_timeout) - *session_timeout = entry->session_timeout; + *session_timeout = + entry->session_timeout; if (acct_interim_interval) - *acct_interim_interval = entry->acct_interim_interval; + *acct_interim_interval = + entry->acct_interim_interval; if (vlan_id) *vlan_id = entry->vlan_id; return entry->accepted; @@ -95,8 +102,8 @@ static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) { if (query == NULL) return; - free(query->auth_msg); - free(query); + os_free(query->auth_msg); + os_free(query); } @@ -113,25 +120,25 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, radius_msg_make_authenticator(msg, addr, ETH_ALEN); - snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, - strlen(buf))) { - printf("Could not add User-Name\n"); + os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add User-Name"); goto fail; } if (!radius_msg_add_attr_user_password( - msg, (u8 *) buf, strlen(buf), + msg, (u8 *) buf, os_strlen(buf), hapd->conf->radius->auth_server->shared_secret, hapd->conf->radius->auth_server->shared_secret_len)) { - printf("Could not add User-Password\n"); + wpa_printf(MSG_DEBUG, "Could not add User-Password"); goto fail; } if (hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); + wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); goto fail; } @@ -139,7 +146,7 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, if (hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); + wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); goto fail; } #endif /* CONFIG_IPV6 */ @@ -147,37 +154,37 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); + os_strlen(hapd->conf->nas_identifier))) { + wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); goto fail; } - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Called-Station-Id\n"); + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); goto fail; } - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(addr)); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); + wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); goto fail; } - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { - printf("Could not add Connect-Info\n"); + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); goto fail; } @@ -186,11 +193,22 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, fail: radius_msg_free(msg); - free(msg); + os_free(msg); return -1; } +/** + * hostapd_allowed_address - Check whether a specified STA can be authenticated + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @msg: Authentication message + * @len: Length of msg in octets + * @session_timeout: Buffer for returning session timeout (from RADIUS) + * @acct_interim_interval: Buffer for returning account interval (from RADIUS) + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id) @@ -203,11 +221,11 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *vlan_id = 0; if (hostapd_maclist_found(hapd->conf->accept_mac, - hapd->conf->num_accept_mac, addr)) + hapd->conf->num_accept_mac, addr, vlan_id)) return HOSTAPD_ACL_ACCEPT; if (hostapd_maclist_found(hapd->conf->deny_mac, - hapd->conf->num_deny_mac, addr)) + hapd->conf->num_deny_mac, addr, vlan_id)) return HOSTAPD_ACL_REJECT; if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) @@ -230,7 +248,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, query = hapd->acl_queries; while (query) { - if (memcmp(query->addr, addr, ETH_ALEN) == 0) { + if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ return HOSTAPD_ACL_PENDING; @@ -242,27 +260,28 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; /* No entry in the cache - query external RADIUS server */ - query = wpa_zalloc(sizeof(*query)); + query = os_zalloc(sizeof(*query)); if (query == NULL) { - printf("malloc for query data failed\n"); + wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } time(&query->timestamp); - memcpy(query->addr, addr, ETH_ALEN); + os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { - printf("Failed to send Access-Request for ACL " - "query.\n"); + wpa_printf(MSG_DEBUG, "Failed to send Access-Request " + "for ACL query."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } - query->auth_msg = malloc(len); + query->auth_msg = os_malloc(len); if (query->auth_msg == NULL) { - printf("Failed to allocate memory for auth frame.\n"); + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "auth frame."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } - memcpy(query->auth_msg, msg, len); + os_memcpy(query->auth_msg, msg, len); query->auth_msg_len = len; query->next = hapd->acl_queries; hapd->acl_queries = query; @@ -285,19 +304,18 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) while (entry) { if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Cached ACL entry for " MACSTR - " has expired.\n", MAC2STR(entry->addr)); + wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR + " has expired.", MAC2STR(entry->addr)); if (prev) prev->next = entry->next; else hapd->acl_cache = entry->next; #ifdef CONFIG_DRIVER_RADIUS_ACL hostapd_set_radius_acl_expire(hapd, entry->addr); -#endif +#endif /* CONFIG_DRIVER_RADIUS_ACL */ tmp = entry; entry = entry->next; - free(tmp); + os_free(tmp); continue; } @@ -316,9 +334,8 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) while (entry) { if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "ACL query for " MACSTR - " has expired.\n", MAC2STR(entry->addr)); + wpa_printf(MSG_DEBUG, "ACL query for " MACSTR + " has expired.", MAC2STR(entry->addr)); if (prev) prev->next = entry->next; else @@ -336,6 +353,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) } +/** + * hostapd_acl_expire - ACL cache expiration callback + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: Not used + */ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; @@ -349,11 +371,19 @@ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) } -/* Return 0 if RADIUS message was a reply to ACL query (and was processed here) - * or -1 if not. */ +/** + * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and + * was processed here) or RADIUS_RX_UNKNOWN if not. + */ static RadiusRxResult hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, + const u8 *shared_secret, size_t shared_secret_len, void *data) { struct hostapd_data *hapd = data; @@ -371,30 +401,30 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (query == NULL) return RADIUS_RX_UNKNOWN; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Found matching Access-Request " - "for RADIUS message (id=%d)\n", query->radius_id); + wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " + "message (id=%d)", query->radius_id); if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { - printf("Incoming RADIUS packet did not have correct " - "authenticator - dropped\n"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have " + "correct authenticator - dropped\n"); return RADIUS_RX_INVALID_AUTHENTICATOR; } if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) { - printf("Unknown RADIUS message code %d to ACL query\n", - msg->hdr->code); + wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " + "query", msg->hdr->code); return RADIUS_RX_UNKNOWN; } /* Insert Accept/Reject info into ACL cache */ - cache = wpa_zalloc(sizeof(*cache)); + cache = os_zalloc(sizeof(*cache)); if (cache == NULL) { - printf("Failed to add ACL cache entry\n"); + wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } time(&cache->timestamp); - memcpy(cache->addr, query->addr, sizeof(cache->addr)); + os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) @@ -406,11 +436,10 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, &cache->acct_interim_interval) == 0 && cache->acct_interim_interval < 60) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Ignored too " - "small Acct-Interim-Interval %d for " - "STA " MACSTR "\n", - cache->acct_interim_interval, - MAC2STR(query->addr)); + wpa_printf(MSG_DEBUG, "Ignored too small " + "Acct-Interim-Interval %d for STA " MACSTR, + cache->acct_interim_interval, + MAC2STR(query->addr)); cache->acct_interim_interval = 0; } @@ -421,15 +450,15 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, hapd->acl_cache = cache; #ifdef CONFIG_DRIVER_RADIUS_ACL - hostapd_set_radius_acl_auth(hapd, query->addr, cache->accepted, - cache->session_timeout); -#else + hostapd_set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); +#else /* CONFIG_DRIVER_RADIUS_ACL */ /* Re-send original authentication frame for 802.11 processing */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Re-sending authentication frame " - "after successful RADIUS ACL query\n"); + wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " + "successful RADIUS ACL query"); ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, WLAN_FC_STYPE_AUTH, NULL); -#endif +#endif /* CONFIG_DRIVER_RADIUS_ACL */ done: if (prev == NULL) @@ -443,6 +472,11 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } +/** + * hostapd_acl_init: Initialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ int hostapd_acl_init(struct hostapd_data *hapd) { if (radius_client_register(hapd->radius, RADIUS_AUTH, @@ -455,6 +489,10 @@ int hostapd_acl_init(struct hostapd_data *hapd) } +/** + * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + */ void hostapd_acl_deinit(struct hostapd_data *hapd) { struct hostapd_acl_query_data *query, *prev; diff --git a/contrib/hostapd/ieee802_11_auth.h b/hostapd/ieee802_11_auth.h index 0eed825e29a40..0eed825e29a40 100644 --- a/contrib/hostapd/ieee802_11_auth.h +++ b/hostapd/ieee802_11_auth.h diff --git a/contrib/hostapd/ieee802_1x.c b/hostapd/ieee802_1x.c index bbd1c1e65bcaa..9b096d4e2a20a 100644 --- a/contrib/hostapd/ieee802_1x.c +++ b/hostapd/ieee802_1x.c @@ -1,6 +1,6 @@ /* - * hostapd / IEEE 802.1X Authenticator - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,13 +13,12 @@ */ #include "includes.h" -#include <assert.h> #include "hostapd.h" #include "ieee802_1x.h" #include "accounting.h" -#include "radius.h" -#include "radius_client.h" +#include "radius/radius.h" +#include "radius/radius_client.h" #include "eapol_sm.h" #include "md5.h" #include "rc4.h" @@ -30,15 +29,16 @@ #include "pmksa_cache.h" #include "driver.h" #include "hw_features.h" -#include "eap.h" +#include "eap_server/eap.h" +#include "ieee802_11_defs.h" -static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, - struct sta_info *sta); +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success); static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 type, u8 *data, size_t datalen) + u8 type, const u8 *data, size_t datalen) { u8 *buf; struct ieee802_1x_hdr *xhdr; @@ -46,32 +46,21 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, int encrypt = 0; len = sizeof(*xhdr) + datalen; - buf = wpa_zalloc(len); + buf = os_zalloc(len); if (buf == NULL) { - printf("malloc() failed for ieee802_1x_send(len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_ERROR, "malloc() failed for " + "ieee802_1x_send(len=%lu)", + (unsigned long) len); return; } -#if 0 - /* TODO: - * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any - * remaining EAP frames, if possible. This would allow rest of the - * frames to be encrypted. This code could be used to request - * encryption from the kernel driver. */ - if (sta->eapol_sm && - sta->eapol_sm->be_auth.state == BE_AUTH_SUCCESS && - sta->eapol_sm->keyTxEnabled) - encrypt = 1; -#endif - xhdr = (struct ieee802_1x_hdr *) buf; xhdr->version = hapd->conf->eapol_version; xhdr->type = type; - xhdr->length = htons(datalen); + xhdr->length = host_to_be16(datalen); if (datalen > 0 && data != NULL) - memcpy(xhdr + 1, data, datalen); + os_memcpy(xhdr + 1, data, datalen); if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; @@ -81,7 +70,7 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, hostapd_send_eapol(hapd, sta->addr, buf, len, encrypt); } - free(buf); + os_free(buf); } @@ -95,13 +84,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, if (authorized) { sta->flags |= WLAN_STA_AUTHORIZED; - res = hostapd_sta_set_flags(hapd, sta->addr, + res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, WLAN_STA_AUTHORIZED, ~0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { sta->flags &= ~WLAN_STA_AUTHORIZED; - res = hostapd_sta_set_flags(hapd, sta->addr, + res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, 0, ~WLAN_STA_AUTHORIZED); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); @@ -117,137 +106,6 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, } -static void ieee802_1x_eap_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct sta_info *sta = eloop_ctx; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "EAP timeout"); - sm->eapTimeout = TRUE; - eapol_sm_step(sm); -} - - -void ieee802_1x_request_identity(struct hostapd_data *hapd, - struct sta_info *sta) -{ - u8 *buf; - struct eap_hdr *eap; - int tlen; - u8 *pos; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (hapd->conf->eap_server) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Integrated EAP server in " - "use - do not generate EAP-Request/Identity\n"); - return; - } - - if (sm == NULL || !sm->eapRestart) - return; - - ieee802_1x_new_auth_session(hapd, sta); - - tlen = sizeof(*eap) + 1 + hapd->conf->eap_req_id_text_len; - - buf = wpa_zalloc(tlen); - if (buf == NULL) { - printf("Could not allocate memory for identity request\n"); - return; - } - - eap = (struct eap_hdr *) buf; - eap->code = EAP_CODE_REQUEST; - eap->identifier = ++sm->currentId; - eap->length = htons(tlen); - pos = (u8 *) (eap + 1); - *pos++ = EAP_TYPE_IDENTITY; - if (hapd->conf->eap_req_id_text) { - memcpy(pos, hapd->conf->eap_req_id_text, - hapd->conf->eap_req_id_text_len); - } - - sm->eapReq = TRUE; - free(sm->last_eap_radius); - sm->last_eap_radius = buf; - sm->last_eap_radius_len = tlen; - - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - eloop_register_timeout(30, 0, ieee802_1x_eap_timeout, sta, NULL); - sm->eapTimeout = FALSE; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Generated EAP Request-Identity for " MACSTR - " (identifier %d, timeout 30)\n", MAC2STR(sta->addr), - eap->identifier); - - sm->eapRestart = FALSE; -} - - -void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - struct eap_hdr eap; - struct eapol_state_machine *sm = sta->eapol_sm; - - memset(&eap, 0, sizeof(eap)); - - eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; - eap.identifier = 1; - if (sm && sm->last_eap_radius) { - struct eap_hdr *hdr = (struct eap_hdr *) sm->last_eap_radius; - eap.identifier = hdr->identifier + 1; - } - eap.length = htons(sizeof(eap)); - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending canned EAP packet %s to " MACSTR - " (identifier %d)\n", success ? "SUCCESS" : "FAILURE", - MAC2STR(sta->addr), eap.identifier); - ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap, - sizeof(eap)); - if (sm) - sm->dot1xAuthEapolFramesTx++; -} - - -void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta) -{ - struct eap_hdr *eap; - struct eapol_state_machine *sm = sta->eapol_sm; - u8 *type; - if (sm == NULL) - return; - - if (sm->last_eap_radius == NULL) { - printf("Error: TxReq called for station " MACSTR ", but there " - "is no EAP request from the authentication server\n", - MAC2STR(sm->addr)); - return; - } - - eap = (struct eap_hdr *) sm->last_eap_radius; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAP Packet to " MACSTR - " (identifier %d)\n", MAC2STR(sm->addr), - eap->identifier); - sm->currentId = eap->identifier; - ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, - sm->last_eap_radius, sm->last_eap_radius_len); - sm->dot1xAuthEapolFramesTx++; - type = (u8 *) (eap + 1); - if (sm->last_eap_radius_len > sizeof(*eap) && - *type == EAP_TYPE_IDENTITY) - sm->dot1xAuthEapolReqIdFramesTx++; - else - sm->dot1xAuthEapolReqFramesTx++; -} - - static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, @@ -263,7 +121,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, return; len = sizeof(*key) + key_len; - buf = wpa_zalloc(sizeof(*hdr) + len); + buf = os_zalloc(sizeof(*hdr) + len); if (buf == NULL) return; @@ -273,9 +131,9 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, key->key_length = htons(key_len); wpa_get_ntp_timestamp(key->replay_counter); - if (hostapd_get_rand(key->key_iv, sizeof(key->key_iv))) { - printf("Could not get random numbers\n"); - free(buf); + if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + wpa_printf(MSG_ERROR, "Could not get random numbers"); + os_free(buf); return; } @@ -288,39 +146,42 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, key->key_index |= BIT(7); } - /* Key is encrypted using "Key-IV + sm->eapol_key_crypt" as the - * RC4-key */ - memcpy((u8 *) (key + 1), key_data, key_len); - ekey_len = sizeof(key->key_iv) + sm->eapol_key_crypt_len; - ekey = malloc(ekey_len); + /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and + * MSK[32..63] is used to sign the message. */ + if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) { + wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting " + "and signing EAPOL-Key"); + os_free(buf); + return; + } + os_memcpy((u8 *) (key + 1), key_data, key_len); + ekey_len = sizeof(key->key_iv) + 32; + ekey = os_malloc(ekey_len); if (ekey == NULL) { - printf("Could not encrypt key\n"); - free(buf); + wpa_printf(MSG_ERROR, "Could not encrypt key"); + os_free(buf); return; } - memcpy(ekey, key->key_iv, sizeof(key->key_iv)); - memcpy(ekey + sizeof(key->key_iv), sm->eapol_key_crypt, - sm->eapol_key_crypt_len); + os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); + os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); rc4((u8 *) (key + 1), key_len, ekey, ekey_len); - free(ekey); + os_free(ekey); /* This header is needed here for HMAC-MD5, but it will be regenerated * in ieee802_1x_send() */ hdr->version = hapd->conf->eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len); - hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len, - buf, sizeof(*hdr) + len, + hdr->length = host_to_be16(len); + hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, key->key_signature); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAPOL-Key to " MACSTR - " (%s index=%d)\n", MAC2STR(sm->addr), - broadcast ? "broadcast" : "unicast", idx); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR + " (%s index=%d)", MAC2STR(sm->addr), + broadcast ? "broadcast" : "unicast", idx); ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapolFramesTx++; - free(buf); + os_free(buf); } @@ -329,7 +190,7 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) { struct hostapd_wep_keys *key; - key = wpa_zalloc(sizeof(*key)); + key = os_zalloc(sizeof(*key)); if (key == NULL) return NULL; @@ -342,23 +203,21 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) key->idx++; if (!key->key[key->idx]) - key->key[key->idx] = malloc(key->default_len); + key->key[key->idx] = os_malloc(key->default_len); if (key->key[key->idx] == NULL || - hostapd_get_rand(key->key[key->idx], key->default_len)) { + os_get_random(key->key[key->idx], key->default_len)) { printf("Could not generate random WEP key (dynamic VLAN).\n"); - free(key->key[key->idx]); + os_free(key->key[key->idx]); key->key[key->idx] = NULL; - free(key); + os_free(key); return NULL; } key->len[key->idx] = key->default_len; - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - printf("%s: Default WEP idx %d for dynamic VLAN\n", - ifname, key->idx); - wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", - key->key[key->idx], key->len[key->idx]); - } + wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", + ifname, key->idx); + wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", + key->key[key->idx], key->len[key->idx]); if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, key->key[key->idx], key->len[key->idx], 1)) @@ -383,21 +242,21 @@ ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, ssid->dyn_vlan_keys[vlan_id]) return ssid->dyn_vlan_keys[vlan_id]; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Creating new group " - "state machine for VLAN ID %lu\n", - (unsigned long) vlan_id); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " + "state machine for VLAN ID %lu", + (unsigned long) vlan_id); ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); if (ifname == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Unknown " - "VLAN ID %lu - cannot create group key state " - "machine\n", (unsigned long) vlan_id); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " + "cannot create group key state machine", + (unsigned long) vlan_id); return NULL; } if (ssid->dyn_vlan_keys == NULL) { int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - ssid->dyn_vlan_keys = wpa_zalloc(size); + ssid->dyn_vlan_keys = os_zalloc(size); if (ssid->dyn_vlan_keys == NULL) return NULL; ssid->max_dyn_vlan_keys = vlan_id; @@ -406,13 +265,13 @@ ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, if (ssid->max_dyn_vlan_keys < vlan_id) { struct hostapd_wep_keys **na; int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - na = realloc(ssid->dyn_vlan_keys, size); + na = os_realloc(ssid->dyn_vlan_keys, size); if (na == NULL) return NULL; ssid->dyn_vlan_keys = na; - memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, - (vlan_id - ssid->max_dyn_vlan_keys) * - sizeof(ssid->dyn_vlan_keys[0])); + os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, + (vlan_id - ssid->max_dyn_vlan_keys) * + sizeof(ssid->dyn_vlan_keys[0])); ssid->max_dyn_vlan_keys = vlan_id; } @@ -428,12 +287,11 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) struct eapol_state_machine *sm = sta->eapol_sm; int vlan_id; - if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt) + if (sm == NULL || !sm->eap_if->eapKeyData) return; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, + MAC2STR(sta->addr)); vlan_id = sta->vlan_id; if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) @@ -453,13 +311,12 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) if (hapd->conf->individual_wep_key_len > 0) { u8 *ikey; - ikey = malloc(hapd->conf->individual_wep_key_len); + ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || - hostapd_get_rand(ikey, - hapd->conf->individual_wep_key_len)) { - printf("Could not generate random individual WEP " - "key.\n"); - free(ikey); + os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + wpa_printf(MSG_ERROR, "Could not generate random " + "individual WEP key."); + os_free(ikey); return; } @@ -475,10 +332,11 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) sta->addr, 0, ikey, hapd->conf->individual_wep_key_len, 1)) { - printf("Could not set individual WEP encryption.\n"); + wpa_printf(MSG_ERROR, "Could not set individual WEP " + "encryption."); } - free(ikey); + os_free(ikey); } } @@ -513,9 +371,41 @@ int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) } +static void ieee802_1x_learn_identity(struct hostapd_data *hapd, + struct eapol_state_machine *sm, + const u8 *eap, size_t len) +{ + const u8 *identity; + size_t identity_len; + + if (len <= sizeof(struct eap_hdr) || + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + return; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity == NULL) + return; + + /* Save station identity for future RADIUS packets */ + os_free(sm->identity); + sm->identity = os_malloc(identity_len + 1); + if (sm->identity == NULL) { + sm->identity_len = 0; + return; + } + + os_memcpy(sm->identity, identity, identity_len); + sm->identity_len = identity_len; + sm->identity[identity_len] = '\0'; + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); + sm->dot1xAuthEapolRespIdFramesRx++; +} + + static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, - u8 *eap, size_t len) + const u8 *eap, size_t len) { struct radius_msg *msg; char buf[128]; @@ -524,8 +414,10 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, if (sm == NULL) return; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Encapsulating EAP message into a RADIUS packet\n"); + ieee802_1x_learn_identity(hapd, sm, eap, len); + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); sm->radius_identifier = radius_client_get_id(hapd->radius); msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, @@ -563,7 +455,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { + os_strlen(hapd->conf->nas_identifier))) { printf("Could not add NAS-Identifier\n"); goto fail; } @@ -573,20 +465,20 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Called-Station-Id\n"); goto fail; } - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Calling-Station-Id\n"); goto fail; } @@ -606,16 +498,17 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } if (sta->flags & WLAN_STA_PREAUTH) { - snprintf(buf, sizeof(buf), "IEEE 802.11i Pre-Authentication"); + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); } else { - snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; } - buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { + (u8 *) buf, os_strlen(buf))) { printf("Could not add Connect-Info\n"); goto fail; } @@ -637,8 +530,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } if (res > 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Copied RADIUS State Attribute\n"); + wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute"); } } @@ -647,7 +539,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, fail: radius_msg_free(msg); - free(msg); + os_free(msg); } @@ -667,6 +559,7 @@ char *eap_type_text(u8 type) case EAP_TYPE_FAST: return "FAST"; case EAP_TYPE_SAKE: return "SAKE"; case EAP_TYPE_PSK: return "PSK"; + case EAP_TYPE_PAX: return "PAX"; default: return "Unknown"; } } @@ -674,85 +567,32 @@ char *eap_type_text(u8 type) static void handle_eap_response(struct hostapd_data *hapd, struct sta_info *sta, struct eap_hdr *eap, - u8 *data, size_t len) + size_t len) { - u8 type; + u8 type, *data; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; - if (eap->identifier != sm->currentId) { - hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "EAP Identifier of the Response-Identity does " - "not match (was %d, expected %d) - ignored", - eap->identifier, sm->currentId); - return; - } + data = (u8 *) (eap + 1); - if (len < 1) { + if (len < sizeof(*eap) + 1) { printf("handle_eap_response: too short response data\n"); return; } - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - - free(sm->last_eap_supp); - sm->last_eap_supp_len = sizeof(*eap) + len; - sm->last_eap_supp = (u8 *) malloc(sm->last_eap_supp_len); - if (sm->last_eap_supp == NULL) { - printf("Could not alloc memory for last EAP Response\n"); - return; - } - memcpy(sm->last_eap_supp, eap, sizeof(*eap)); - memcpy(sm->last_eap_supp + sizeof(*eap), data, len); - sm->eap_type_supp = type = data[0]; - data++; - len--; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " "id=%d len=%d) from STA: EAP Response-%s (%d)", - eap->code, eap->identifier, ntohs(eap->length), + eap->code, eap->identifier, be_to_host16(eap->length), eap_type_text(type), type); - if (type == EAP_TYPE_IDENTITY) { - char *buf, *pos; - size_t i; - buf = malloc(4 * len + 1); - if (buf) { - pos = buf; - for (i = 0; i < len; i++) { - if (data[i] >= 32 && data[i] < 127) - *pos++ = data[i]; - else { - snprintf(pos, 5, "{%02x}", data[i]); - pos += 4; - } - } - *pos = '\0'; - hostapd_logger(hapd, sm->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "STA identity '%s'", buf); - free(buf); - } - - sm->rx_identity = TRUE; - sm->dot1xAuthEapolRespIdFramesRx++; - - /* Save station identity for future RADIUS packets */ - free(sm->identity); - sm->identity = malloc(len + 1); - if (sm->identity) { - memcpy(sm->identity, data, len); - sm->identity[len] = '\0'; - sm->identity_len = len; - } - } else - sm->dot1xAuthEapolRespFramesRx++; + sm->dot1xAuthEapolRespFramesRx++; + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); sm->eapolEap = TRUE; } @@ -771,45 +611,51 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, eap = (struct eap_hdr *) buf; - eap_len = ntohs(eap->length); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " EAP: code=%d identifier=%d length=%d", - eap->code, eap->identifier, eap_len); + eap_len = be_to_host16(eap->length); + wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, eap_len); if (eap_len < sizeof(*eap)) { - printf(" Invalid EAP length\n"); + wpa_printf(MSG_DEBUG, " Invalid EAP length"); return; } else if (eap_len > len) { - printf(" Too short frame to contain this EAP packet\n"); + wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP " + "packet"); return; } else if (eap_len < len) { - printf(" Ignoring %lu extra bytes after EAP packet\n", - (unsigned long) len - eap_len); + wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP " + "packet", (unsigned long) len - eap_len); } - eap_len -= sizeof(*eap); - switch (eap->code) { case EAP_CODE_REQUEST: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (request)\n"); + wpa_printf(MSG_DEBUG, " (request)"); return; case EAP_CODE_RESPONSE: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (response)\n"); - handle_eap_response(hapd, sta, eap, (u8 *) (eap + 1), eap_len); + wpa_printf(MSG_DEBUG, " (response)"); + handle_eap_response(hapd, sta, eap, eap_len); break; case EAP_CODE_SUCCESS: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (success)\n"); + wpa_printf(MSG_DEBUG, " (success)"); return; case EAP_CODE_FAILURE: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (failure)\n"); + wpa_printf(MSG_DEBUG, " (failure)"); return; default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (unknown code)\n"); + wpa_printf(MSG_DEBUG, " (unknown code)"); return; } } -/* Process the EAPOL frames from the Supplicant */ +/** + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant + * @hapd: hostapd BSS data + * @sa: Source address (sender of the EAPOL frame) + * @buf: EAPOL frame + * @len: Length of buf in octets + * + * This function is called for each incoming EAPOL frame from the interface + */ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len) { @@ -819,12 +665,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, u16 datalen; struct rsn_pmksa_cache_entry *pmksa; - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->wps_state) return; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: %lu bytes from " MACSTR "\n", - (unsigned long) len, MAC2STR(sa)); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, + (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); if (!sta) { printf(" no station information available\n"); @@ -837,10 +683,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } hdr = (struct ieee802_1x_hdr *) buf; - datalen = ntohs(hdr->length); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " IEEE 802.1X: version=%d type=%d length=%d\n", - hdr->version, hdr->type, datalen); + datalen = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", + hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { printf(" frame too short for this IEEE 802.1X packet\n"); @@ -849,10 +694,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } if (len - sizeof(*hdr) > datalen) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " ignoring %lu extra octets after IEEE 802.1X " - "packet\n", - (unsigned long) len - sizeof(*hdr) - datalen); + wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after " + "IEEE 802.1X packet", + (unsigned long) len - sizeof(*hdr) - datalen); } if (sta->eapol_sm) { @@ -870,14 +714,31 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } - if (!hapd->conf->ieee802_1x || - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_PSK) + if ((!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) return; if (!sta->eapol_sm) { - sta->eapol_sm = eapol_sm_alloc(hapd, sta); + sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, + sta->flags & WLAN_STA_PREAUTH, + sta); if (!sta->eapol_sm) return; + +#ifdef CONFIG_WPS + if (!hapd->conf->ieee802_1x && + ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * STA initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; } /* since we support version 1, we can ignore version field and proceed @@ -896,6 +757,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " "from STA"); + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, @@ -915,49 +777,71 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, "from STA"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; break; case IEEE802_1X_TYPE_EAPOL_KEY: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " EAPOL-Key\n"); + wpa_printf(MSG_DEBUG, " EAPOL-Key"); if (!(sta->flags & WLAN_STA_AUTHORIZED)) { - printf(" Dropped key data from unauthorized " - "Supplicant\n"); + wpa_printf(MSG_DEBUG, " Dropped key data from " + "unauthorized Supplicant"); break; } break; case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " EAPOL-Encapsulated-ASF-Alert\n"); + wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); /* TODO: implement support for this; show data */ break; default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " unknown IEEE 802.1X packet type\n"); + wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; break; } - eapol_sm_step(sta->eapol_sm); + eapol_auth_step(sta->eapol_sm); } +/** + * ieee802_1x_new_station - Start IEEE 802.1X authentication + * @hapd: hostapd BSS data + * @sta: The station + * + * This function is called to start IEEE 802.1X authentication when a new + * station completes IEEE 802.11 association. + */ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) { struct rsn_pmksa_cache_entry *pmksa; int reassoc = 1; - - if (!hapd->conf->ieee802_1x || - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_PSK) + int force_1x = 0; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->conf->wpa && + (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + /* + * Need to enable IEEE 802.1X/EAPOL state machines for possible + * WPS handshake even if IEEE 802.1X/EAPOL is not used for + * authentication in this BSS. + */ + force_1x = 1; + } +#endif /* CONFIG_WPS */ + + if ((!force_1x && !hapd->conf->ieee802_1x) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) return; if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "start authentication"); - sta->eapol_sm = eapol_sm_alloc(hapd, sta); + sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, + sta->flags & WLAN_STA_PREAUTH, + sta); if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -968,7 +852,18 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) reassoc = 0; } - sta->eapol_sm->portEnabled = TRUE; +#ifdef CONFIG_WPS + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { @@ -980,7 +875,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) /* Setup EAPOL state machines to already authenticated state * because of existing PMKSA information in the cache. */ sta->eapol_sm->keyRun = TRUE; - sta->eapol_sm->keyAvailable = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; @@ -1000,7 +895,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) */ sta->eapol_sm->reAuthenticate = TRUE; } - eapol_sm_step(sta->eapol_sm); + eapol_auth_step(sta->eapol_sm); } } @@ -1011,33 +906,34 @@ void ieee802_1x_free_radius_class(struct radius_class_data *class) if (class == NULL) return; for (i = 0; i < class->count; i++) - free(class->attr[i].data); - free(class->attr); + os_free(class->attr[i].data); + os_free(class->attr); class->attr = NULL; class->count = 0; } int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - struct radius_class_data *src) + const struct radius_class_data *src) { size_t i; if (src->attr == NULL) return 0; - dst->attr = wpa_zalloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; dst->count = 0; for (i = 0; i < src->count; i++) { - dst->attr[i].data = malloc(src->attr[i].len); + dst->attr[i].data = os_malloc(src->attr[i].len); if (dst->attr[i].data == NULL) break; dst->count++; - memcpy(dst->attr[i].data, src->attr[i].data, src->attr[i].len); + os_memcpy(dst->attr[i].data, src->attr[i].data, + src->attr[i].len); dst->attr[i].len = src->attr[i].len; } @@ -1049,8 +945,6 @@ void ieee802_1x_free_station(struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - if (sm == NULL) return; @@ -1058,16 +952,12 @@ void ieee802_1x_free_station(struct sta_info *sta) if (sm->last_recv_radius) { radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); + os_free(sm->last_recv_radius); } - free(sm->last_eap_supp); - free(sm->last_eap_radius); - free(sm->identity); + os_free(sm->identity); ieee802_1x_free_radius_class(&sm->radius_class); - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - eapol_sm_free(sm); + eapol_auth_free(sm); } @@ -1084,7 +974,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, if (sm == NULL || sm->last_recv_radius == NULL) { if (sm) - sm->eapNoReq = TRUE; + sm->eap_if->aaaEapNoReq = TRUE; return; } @@ -1092,16 +982,13 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, eap = radius_msg_get_eap(msg, &len); if (eap == NULL) { - /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: + /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message * attribute */ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "could not extract " "EAP-Message from RADIUS message"); - free(sm->last_eap_radius); - sm->last_eap_radius = NULL; - sm->last_eap_radius_len = 0; - sm->eapNoReq = TRUE; + sm->eap_if->aaaEapNoReq = TRUE; return; } @@ -1109,8 +996,8 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - free(eap); - sm->eapNoReq = TRUE; + os_free(eap); + sm->eap_if->aaaEapNoReq = TRUE; return; } @@ -1122,42 +1009,43 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, case EAP_CODE_REQUEST: if (eap_type >= 0) sm->eap_type_authsrv = eap_type; - snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", - eap_type >= 0 ? eap_type_text(eap_type) : "??", - eap_type); + os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); break; case EAP_CODE_RESPONSE: - snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", - eap_type >= 0 ? eap_type_text(eap_type) : "??", - eap_type); + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); break; case EAP_CODE_SUCCESS: - snprintf(buf, sizeof(buf), "EAP Success"); + os_strlcpy(buf, "EAP Success", sizeof(buf)); break; case EAP_CODE_FAILURE: - snprintf(buf, sizeof(buf), "EAP Failure"); + os_strlcpy(buf, "EAP Failure", sizeof(buf)); break; default: - snprintf(buf, sizeof(buf), "unknown EAP code"); + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); break; } buf[sizeof(buf) - 1] = '\0'; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " "id=%d len=%d) from RADIUS server: %s", - hdr->code, hdr->identifier, ntohs(hdr->length), buf); - sm->eapReq = TRUE; + hdr->code, hdr->identifier, be_to_host16(hdr->length), + buf); + sm->eap_if->aaaEapReq = TRUE; - free(sm->last_eap_radius); - sm->last_eap_radius = eap; - sm->last_eap_radius_len = len; + wpabuf_free(sm->eap_if->aaaEapReqData); + sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); } static void ieee802_1x_get_keys(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len) + const u8 *shared_secret, + size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; struct eapol_state_machine *sm = sta->eapol_sm; @@ -1167,32 +1055,29 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, keys = radius_msg_get_ms_keys(msg, req, shared_secret, shared_secret_len); - if (keys) { - if (keys->send) { - wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", - keys->send, keys->send_len); - } - if (keys->recv) { - wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", - keys->recv, keys->recv_len); + if (keys && keys->send && keys->recv) { + size_t len = keys->send_len + keys->recv_len; + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); + + os_free(sm->eap_if->aaaEapKeyData); + sm->eap_if->aaaEapKeyData = os_malloc(len); + if (sm->eap_if->aaaEapKeyData) { + os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv, + keys->recv_len); + os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len, + keys->send, keys->send_len); + sm->eap_if->aaaEapKeyDataLen = len; + sm->eap_if->aaaEapKeyAvailable = TRUE; } + } - if (keys->send && keys->recv) { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_sign = keys->send; - sm->eapol_key_sign_len = keys->send_len; - sm->eapol_key_crypt = keys->recv; - sm->eapol_key_crypt_len = keys->recv_len; - if (hapd->default_wep_key || - hapd->conf->individual_wep_key_len > 0 || - hapd->conf->wpa) - sta->eapol_sm->keyAvailable = TRUE; - } else { - free(keys->send); - free(keys->recv); - } - free(keys); + if (keys) { + os_free(keys->send); + os_free(keys->recv); + os_free(keys); } } @@ -1217,7 +1102,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = wpa_zalloc(count * sizeof(struct radius_attr_data)); + nclass = os_zalloc(count * sizeof(struct radius_attr_data)); if (nclass == NULL) return; @@ -1234,21 +1119,21 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, } } while (class_len < 1); - nclass[nclass_count].data = malloc(class_len); + nclass[nclass_count].data = os_malloc(class_len); if (nclass[nclass_count].data == NULL) break; - memcpy(nclass[nclass_count].data, class, class_len); + os_memcpy(nclass[nclass_count].data, class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } sm->radius_class.attr = nclass; sm->radius_class.count = nclass_count; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Stored %lu RADIUS " - "Class attributes for " MACSTR "\n", - (unsigned long) sm->radius_class.count, - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class " + "attributes for " MACSTR, + (unsigned long) sm->radius_class.count, + MAC2STR(sta->addr)); } @@ -1268,11 +1153,11 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, NULL) < 0) return; - identity = malloc(len + 1); + identity = os_malloc(len + 1); if (identity == NULL) return; - memcpy(identity, buf, len); + os_memcpy(identity, buf, len); identity[len] = '\0'; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1281,7 +1166,7 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, sm->identity ? (char *) sm->identity : "N/A", (char *) identity); - free(sm->identity); + os_free(sm->identity); sm->identity = identity; sm->identity_len = len; } @@ -1320,25 +1205,31 @@ ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) } -/* Process the RADIUS frames from Authentication Server */ +/** + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ static RadiusRxResult ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, + const u8 *shared_secret, size_t shared_secret_len, void *data) { struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set, old_vlanid = 0; - int eap_timeout; struct eapol_state_machine *sm; int override_eapReq = 0; sm = ieee802_1x_search_radius_identifier(hapd, msg->hdr->identifier); if (sm == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Could not " - "find matching station for this RADIUS " - "message\n"); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " + "station for this RADIUS message"); return RADIUS_RX_UNKNOWN; } sta = sm->sta; @@ -1349,9 +1240,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, 0) < 0 && radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Allowing RADIUS " - "Access-Reject without Message-Authenticator " - "since it does not include EAP-Message\n"); + wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without " + "Message-Authenticator since it does not include " + "EAP-Message"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) { printf("Incoming RADIUS packet did not have correct " @@ -1367,13 +1258,12 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } sm->radius_identifier = -1; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RADIUS packet matching with station " MACSTR "\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, + MAC2STR(sta->addr)); if (sm->last_recv_radius) { radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); + os_free(sm->last_recv_radius); } sm->last_recv_radius = msg; @@ -1435,13 +1325,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); - sm->eapSuccess = TRUE; + sm->eap_if->aaaSuccess = TRUE; override_eapReq = 1; ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); - if (sm->keyAvailable && + if (sm->eap_if->eapKeyAvailable && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? (int) session_timeout : -1, sm) == 0) { @@ -1451,56 +1341,40 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } break; case RADIUS_CODE_ACCESS_REJECT: - sm->eapFail = TRUE; + sm->eap_if->aaaFail = TRUE; override_eapReq = 1; break; case RADIUS_CODE_ACCESS_CHALLENGE: + sm->eap_if->aaaEapReq = TRUE; if (session_timeout_set) { /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ - eap_timeout = session_timeout; - } else - eap_timeout = 30; - hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "using EAP timeout of %d seconds%s", - eap_timeout, - session_timeout_set ? " (from RADIUS)" : ""); - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - eloop_register_timeout(eap_timeout, 0, ieee802_1x_eap_timeout, - sta, NULL); - sm->eapTimeout = FALSE; + sm->eap_if->aaaMethodTimeout = session_timeout; + hostapd_logger(hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "using EAP timeout of %d seconds (from " + "RADIUS)", + sm->eap_if->aaaMethodTimeout); + } else { + /* + * Use dynamic retransmission behavior per EAP + * specification. + */ + sm->eap_if->aaaMethodTimeout = 0; + } break; } ieee802_1x_decapsulate_radius(hapd, sta); if (override_eapReq) - sm->eapReq = FALSE; + sm->eap_if->aaaEapReq = FALSE; - eapol_sm_step(sm); + eapol_auth_step(sm); return RADIUS_RX_QUEUED; } -/* Handler for EAPOL Backend Authentication state machine sendRespToServer. - * Forward the EAP Response from Supplicant to Authentication Server. */ -void ieee802_1x_send_resp_to_server(struct hostapd_data *hapd, - struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - if (hapd->conf->eap_server) { - eap_set_eapRespData(sm->eap, sm->last_eap_supp, - sm->last_eap_supp_len); - } else { - ieee802_1x_encapsulate_radius(hapd, sta, sm->last_eap_supp, - sm->last_eap_supp_len); - } -} - - void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; @@ -1512,15 +1386,25 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) if (sm->last_recv_radius) { radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); + os_free(sm->last_recv_radius); sm->last_recv_radius = NULL; } - free(sm->last_eap_supp); - sm->last_eap_supp = NULL; - sm->last_eap_supp_len = 0; - free(sm->last_eap_radius); - sm->last_eap_radius = NULL; - sm->last_eap_radius_len = 0; + + if (sm->eap_if->eapTimeout) { + /* + * Disconnect the STA since it did not reply to the last EAP + * request and we cannot continue EAP processing (EAP-Failure + * could only be sent if the EAP peer actually replied). + */ + sm->eap_if->portEnabled = FALSE; + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_AUTHORIZED); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); + sta->timeout_next = STA_REMOVE; + } } @@ -1555,12 +1439,10 @@ void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) sm->eap_type_authsrv, eap_type_text(sm->eap_type_authsrv), sm->eap_type_supp, eap_type_text(sm->eap_type_supp)); - fprintf(f, "%scached_packets=%s%s%s\n", prefix, - sm->last_recv_radius ? "[RX RADIUS]" : "", - sm->last_eap_radius ? "[EAP RADIUS]" : "", - sm->last_eap_supp ? "[EAP SUPPLICANT]" : ""); + fprintf(f, "%scached_packets=%s\n", prefix, + sm->last_recv_radius ? "[RX RADIUS]" : ""); - eapol_sm_dump_state(f, prefix, sm); + eapol_auth_dump_state(f, prefix, sm); } #endif /* HOSTAPD_DUMP_STATE */ @@ -1570,13 +1452,13 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) if (hapd->conf->default_wep_key_len < 1) return 0; - free(hapd->default_wep_key); - hapd->default_wep_key = malloc(hapd->conf->default_wep_key_len); + os_free(hapd->default_wep_key); + hapd->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); if (hapd->default_wep_key == NULL || - hostapd_get_rand(hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { + os_get_random(hapd->default_wep_key, + hapd->conf->default_wep_key_len)) { printf("Could not generate random WEP key.\n"); - free(hapd->default_wep_key); + os_free(hapd->default_wep_key); hapd->default_wep_key = NULL; return -1; } @@ -1593,8 +1475,8 @@ static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { if (sta->eapol_sm) { - sta->eapol_sm->keyAvailable = TRUE; - eapol_sm_step(sta->eapol_sm); + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + eapol_auth_step(sta->eapol_sm); } return 0; } @@ -1610,14 +1492,14 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) else hapd->default_wep_key_idx++; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: New default WEP " - "key index %d\n", hapd->default_wep_key_idx); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", + hapd->default_wep_key_idx); if (ieee802_1x_rekey_broadcast(hapd)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to generate a " "new broadcast key"); - free(hapd->default_wep_key); + os_free(hapd->default_wep_key); hapd->default_wep_key = NULL; return; } @@ -1631,7 +1513,7 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); - free(hapd->default_wep_key); + os_free(hapd->default_wep_key); hapd->default_wep_key = NULL; return; } @@ -1645,9 +1527,174 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) } +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, + const u8 *data, size_t datalen) +{ + ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +} + + +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, + const u8 *data, size_t datalen) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + + ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); +} + + +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, + int preauth) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + if (preauth) + rsn_preauth_finished(hapd, sta, success); + else + ieee802_1x_finished(hapd, sta, success); +} + + +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct hostapd_data *hapd = ctx; + const struct hostapd_eap_user *eap_user; + int i, count; + + eap_user = hostapd_get_eap_user(hapd->conf, identity, + identity_len, phase2); + if (eap_user == NULL) + return -1; + + os_memset(user, 0, sizeof(*user)); + user->phase2 = phase2; + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + + return 0; +} + + +static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return 0; + return 1; +} + + +static void ieee802_1x_logger(void *ctx, const u8 *addr, + eapol_logger_level level, const char *txt) +{ + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case EAPOL_LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case EAPOL_LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case EAPOL_LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", + txt); +} + + +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, + int authorized) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_set_sta_authorized(hapd, sta, authorized); +} + + +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_abort_auth(hapd, sta); +} + + +static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_tx_key(hapd, sta); +} + + int ieee802_1x_init(struct hostapd_data *hapd) { int i; + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + os_memset(&conf, 0, sizeof(conf)); + conf.hapd = hapd; + conf.eap_reauth_period = hapd->conf->eap_reauth_period; + conf.wpa = hapd->conf->wpa; + conf.individual_wep_key_len = hapd->conf->individual_wep_key_len; + conf.eap_server = hapd->conf->eap_server; + conf.ssl_ctx = hapd->ssl_ctx; + conf.eap_sim_db_priv = hapd->eap_sim_db_priv; + conf.eap_req_id_text = hapd->conf->eap_req_id_text; + conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; + conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; + conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; + conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info; + conf.eap_fast_prov = hapd->conf->eap_fast_prov; + conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; + conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; + conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; + conf.tnc = hapd->conf->tnc; + conf.wps = hapd->wps; + + os_memset(&cb, 0, sizeof(cb)); + cb.eapol_send = ieee802_1x_eapol_send; + cb.aaa_send = ieee802_1x_aaa_send; + cb.finished = _ieee802_1x_finished; + cb.get_eap_user = ieee802_1x_get_eap_user; + cb.sta_entry_alive = ieee802_1x_sta_entry_alive; + cb.logger = ieee802_1x_logger; + cb.set_port_authorized = ieee802_1x_set_port_authorized; + cb.abort_auth = _ieee802_1x_abort_auth; + cb.tx_key = _ieee802_1x_tx_key; + + hapd->eapol_auth = eapol_auth_init(&conf, &cb); + if (hapd->eapol_auth == NULL) + return -1; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1)) @@ -1681,6 +1728,9 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) hostapd_set_ieee8021x(hapd->conf->iface, hapd, 0); + + eapol_auth_deinit(hapd->eapol_auth); + hapd->eapol_auth = NULL; } @@ -1693,28 +1743,6 @@ int ieee802_1x_reconfig(struct hostapd_data *hapd, } -static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, - struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: station " MACSTR " - new auth session, " - "clearing State\n", MAC2STR(sta->addr)); - - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); - sm->last_recv_radius = NULL; - } - - sm->eapSuccess = FALSE; - sm->eapFail = FALSE; -} - - int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len, int ack) { @@ -1722,28 +1750,30 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_1x_hdr *xhdr; struct ieee802_1x_eapol_key *key; u8 *pos; + const unsigned char rfc1042_hdr[ETH_ALEN] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; if (sta == NULL) return -1; - if (len < sizeof(*hdr) + sizeof(rfc1042_header) + 2 + sizeof(*xhdr)) + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr)) return 0; hdr = (struct ieee80211_hdr *) buf; pos = (u8 *) (hdr + 1); - if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) + if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0) return 0; - pos += sizeof(rfc1042_header); - if (((pos[0] << 8) | pos[1]) != ETH_P_PAE) + pos += sizeof(rfc1042_hdr); + if (WPA_GET_BE16(pos) != ETH_P_PAE) return 0; pos += 2; xhdr = (struct ieee802_1x_hdr *) pos; pos += sizeof(*xhdr); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: " MACSTR - " TX status - version=%d type=%d length=%d - ack=%d\n", - MAC2STR(sta->addr), xhdr->version, xhdr->type, - ntohs(xhdr->length), ack); + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " + "type=%d length=%d - ack=%d", + MAC2STR(sta->addr), xhdr->version, xhdr->type, + be_to_host16(xhdr->length), ack); /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant * or Authenticator state machines, but EAPOL-Key packets are not @@ -1795,13 +1825,13 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, } -u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len) +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { if (sm == NULL) return NULL; - *len = sm->eapol_key_crypt_len; - return sm->eapol_key_crypt; + *len = sm->eap_if->eapKeyDataLen; + return sm->eap_if->eapKeyData; } @@ -1810,8 +1840,8 @@ void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, { if (sm == NULL) return; - sm->portEnabled = enabled ? TRUE : FALSE; - eapol_sm_step(sm); + sm->eap_if->portEnabled = enabled ? TRUE : FALSE; + eapol_auth_step(sm); } @@ -1821,7 +1851,7 @@ void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, if (sm == NULL) return; sm->portValid = valid ? TRUE : FALSE; - eapol_sm_step(sm); + eapol_auth_step(sm); } @@ -1858,131 +1888,133 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, if (sm == NULL) return 0; - ret = snprintf(buf + len, buflen - len, - "dot1xPaePortNumber=%d\n" - "dot1xPaePortProtocolVersion=%d\n" - "dot1xPaePortCapabilities=1\n" - "dot1xPaePortInitialize=%d\n" - "dot1xPaePortReauthenticate=FALSE\n", - sta->aid, - EAPOL_VERSION, - sm->initialize); + ret = os_snprintf(buf + len, buflen - len, + "dot1xPaePortNumber=%d\n" + "dot1xPaePortProtocolVersion=%d\n" + "dot1xPaePortCapabilities=1\n" + "dot1xPaePortInitialize=%d\n" + "dot1xPaePortReauthenticate=FALSE\n", + sta->aid, + EAPOL_VERSION, + sm->initialize); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthConfigTable */ - ret = snprintf(buf + len, buflen - len, - "dot1xAuthPaeState=%d\n" - "dot1xAuthBackendAuthState=%d\n" - "dot1xAuthAdminControlledDirections=%d\n" - "dot1xAuthOperControlledDirections=%d\n" - "dot1xAuthAuthControlledPortStatus=%d\n" - "dot1xAuthAuthControlledPortControl=%d\n" - "dot1xAuthQuietPeriod=%u\n" - "dot1xAuthServerTimeout=%u\n" - "dot1xAuthReAuthPeriod=%u\n" - "dot1xAuthReAuthEnabled=%s\n" - "dot1xAuthKeyTxEnabled=%s\n", - sm->auth_pae_state + 1, - sm->be_auth_state + 1, - sm->adminControlledDirections, - sm->operControlledDirections, - sm->authPortStatus, - sm->portControl, - sm->quietPeriod, - sm->serverTimeout, - sm->reAuthPeriod, - bool_txt(sm->reAuthEnabled), - bool_txt(sm->keyTxEnabled)); + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthPaeState=%d\n" + "dot1xAuthBackendAuthState=%d\n" + "dot1xAuthAdminControlledDirections=%d\n" + "dot1xAuthOperControlledDirections=%d\n" + "dot1xAuthAuthControlledPortStatus=%d\n" + "dot1xAuthAuthControlledPortControl=%d\n" + "dot1xAuthQuietPeriod=%u\n" + "dot1xAuthServerTimeout=%u\n" + "dot1xAuthReAuthPeriod=%u\n" + "dot1xAuthReAuthEnabled=%s\n" + "dot1xAuthKeyTxEnabled=%s\n", + sm->auth_pae_state + 1, + sm->be_auth_state + 1, + sm->adminControlledDirections, + sm->operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->quietPeriod, + sm->serverTimeout, + sm->reAuthPeriod, + bool_txt(sm->reAuthEnabled), + bool_txt(sm->keyTxEnabled)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthStatsTable */ - ret = snprintf(buf + len, buflen - len, - "dot1xAuthEapolFramesRx=%u\n" - "dot1xAuthEapolFramesTx=%u\n" - "dot1xAuthEapolStartFramesRx=%u\n" - "dot1xAuthEapolLogoffFramesRx=%u\n" - "dot1xAuthEapolRespIdFramesRx=%u\n" - "dot1xAuthEapolRespFramesRx=%u\n" - "dot1xAuthEapolReqIdFramesTx=%u\n" - "dot1xAuthEapolReqFramesTx=%u\n" - "dot1xAuthInvalidEapolFramesRx=%u\n" - "dot1xAuthEapLengthErrorFramesRx=%u\n" - "dot1xAuthLastEapolFrameVersion=%u\n" - "dot1xAuthLastEapolFrameSource=" MACSTR "\n", - sm->dot1xAuthEapolFramesRx, - sm->dot1xAuthEapolFramesTx, - sm->dot1xAuthEapolStartFramesRx, - sm->dot1xAuthEapolLogoffFramesRx, - sm->dot1xAuthEapolRespIdFramesRx, - sm->dot1xAuthEapolRespFramesRx, - sm->dot1xAuthEapolReqIdFramesTx, - sm->dot1xAuthEapolReqFramesTx, - sm->dot1xAuthInvalidEapolFramesRx, - sm->dot1xAuthEapLengthErrorFramesRx, - sm->dot1xAuthLastEapolFrameVersion, - MAC2STR(sm->addr)); + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEapolFramesRx=%u\n" + "dot1xAuthEapolFramesTx=%u\n" + "dot1xAuthEapolStartFramesRx=%u\n" + "dot1xAuthEapolLogoffFramesRx=%u\n" + "dot1xAuthEapolRespIdFramesRx=%u\n" + "dot1xAuthEapolRespFramesRx=%u\n" + "dot1xAuthEapolReqIdFramesTx=%u\n" + "dot1xAuthEapolReqFramesTx=%u\n" + "dot1xAuthInvalidEapolFramesRx=%u\n" + "dot1xAuthEapLengthErrorFramesRx=%u\n" + "dot1xAuthLastEapolFrameVersion=%u\n" + "dot1xAuthLastEapolFrameSource=" MACSTR "\n", + sm->dot1xAuthEapolFramesRx, + sm->dot1xAuthEapolFramesTx, + sm->dot1xAuthEapolStartFramesRx, + sm->dot1xAuthEapolLogoffFramesRx, + sm->dot1xAuthEapolRespIdFramesRx, + sm->dot1xAuthEapolRespFramesRx, + sm->dot1xAuthEapolReqIdFramesTx, + sm->dot1xAuthEapolReqFramesTx, + sm->dot1xAuthInvalidEapolFramesRx, + sm->dot1xAuthEapLengthErrorFramesRx, + sm->dot1xAuthLastEapolFrameVersion, + MAC2STR(sm->addr)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthDiagTable */ - ret = snprintf(buf + len, buflen - len, - "dot1xAuthEntersConnecting=%u\n" - "dot1xAuthEapLogoffsWhileConnecting=%u\n" - "dot1xAuthEntersAuthenticating=%u\n" - "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" - "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" - "dot1xAuthAuthFailWhileAuthenticating=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" - "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" - "dot1xAuthBackendResponses=%u\n" - "dot1xAuthBackendAccessChallenges=%u\n" - "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" - "dot1xAuthBackendAuthSuccesses=%u\n" - "dot1xAuthBackendAuthFails=%u\n", - sm->authEntersConnecting, - sm->authEapLogoffsWhileConnecting, - sm->authEntersAuthenticating, - sm->authAuthSuccessesWhileAuthenticating, - sm->authAuthTimeoutsWhileAuthenticating, - sm->authAuthFailWhileAuthenticating, - sm->authAuthEapStartsWhileAuthenticating, - sm->authAuthEapLogoffWhileAuthenticating, - sm->authAuthReauthsWhileAuthenticated, - sm->authAuthEapStartsWhileAuthenticated, - sm->authAuthEapLogoffWhileAuthenticated, - sm->backendResponses, - sm->backendAccessChallenges, - sm->backendOtherRequestsToSupplicant, - sm->backendAuthSuccesses, - sm->backendAuthFails); + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEntersConnecting=%u\n" + "dot1xAuthEapLogoffsWhileConnecting=%u\n" + "dot1xAuthEntersAuthenticating=%u\n" + "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" + "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" + "dot1xAuthAuthFailWhileAuthenticating=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" + "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" + "dot1xAuthBackendResponses=%u\n" + "dot1xAuthBackendAccessChallenges=%u\n" + "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" + "dot1xAuthBackendAuthSuccesses=%u\n" + "dot1xAuthBackendAuthFails=%u\n", + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; /* dot1xAuthSessionStatsTable */ - ret = snprintf(buf + len, buflen - len, - /* TODO: dot1xAuthSessionOctetsRx */ - /* TODO: dot1xAuthSessionOctetsTx */ - /* TODO: dot1xAuthSessionFramesRx */ - /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" - "dot1xAuthSessionAuthenticMethod=%d\n" - "dot1xAuthSessionTime=%u\n" - "dot1xAuthSessionTerminateCause=999\n" - "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, - wpa_auth_sta_key_mgmt(sta->wpa_sm) == - WPA_KEY_MGMT_IEEE8021X ? 1 : 2, - (unsigned int) (time(NULL) - sta->acct_session_start), - sm->identity); + ret = os_snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ + "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", + sta->acct_session_id_hi, sta->acct_session_id_lo, + (wpa_key_mgmt_wpa_ieee8021x( + wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? + 1 : 2, + (unsigned int) (time(NULL) - + sta->acct_session_start), + sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; @@ -1991,16 +2023,16 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, } -void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success) { - u8 *key; + const u8 *key; size_t len; /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key && + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (success && key && len >= PMK_LEN && wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, diff --git a/contrib/hostapd/ieee802_1x.h b/hostapd/ieee802_1x.h index 70b21de700460..94cff93563724 100644 --- a/contrib/hostapd/ieee802_1x.h +++ b/hostapd/ieee802_1x.h @@ -1,6 +1,6 @@ /* - * hostapd / IEEE 802.1X Authenticator - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,7 +15,13 @@ #ifndef IEEE802_1X_H #define IEEE802_1X_H -/* draft-congdon-radius-8021x-20.txt */ +struct hostapd_data; +struct sta_info; +struct eapol_state_machine; +struct hostapd_config; +struct hostapd_bss_config; + +/* RFC 3580, 4. RC4 EAPOL-Key Frame */ struct ieee802_1x_eapol_key { u8 type; @@ -45,14 +51,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); -void ieee802_1x_request_identity(struct hostapd_data *hapd, - struct sta_info *sta); -void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, - int success); -void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_send_resp_to_server(struct hostapd_data *hapd, - struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); @@ -67,7 +66,7 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); -u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len); +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, @@ -77,14 +76,15 @@ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen); void hostapd_get_ntp_timestamp(u8 *buf); -void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success); char *eap_type_text(u8 type); struct radius_class_data; void ieee802_1x_free_radius_class(struct radius_class_data *class); int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - struct radius_class_data *src); + const struct radius_class_data *src); + +const char *radius_mode_txt(struct hostapd_data *hapd); +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); #endif /* IEEE802_1X_H */ diff --git a/contrib/hostapd/logwatch/README b/hostapd/logwatch/README index 3cba511909632..3cba511909632 100644 --- a/contrib/hostapd/logwatch/README +++ b/hostapd/logwatch/README diff --git a/contrib/hostapd/logwatch/hostapd b/hostapd/logwatch/hostapd index 97b24ef6e1b84..97b24ef6e1b84 100755 --- a/contrib/hostapd/logwatch/hostapd +++ b/hostapd/logwatch/hostapd diff --git a/contrib/hostapd/logwatch/hostapd.conf b/hostapd/logwatch/hostapd.conf index 5bebe6ad2c1b6..5bebe6ad2c1b6 100644 --- a/contrib/hostapd/logwatch/hostapd.conf +++ b/hostapd/logwatch/hostapd.conf diff --git a/contrib/hostapd/mlme.c b/hostapd/mlme.c index 3d4cf21a45b43..d883931cf4146 100644 --- a/contrib/hostapd/mlme.c +++ b/hostapd/mlme.c @@ -18,7 +18,6 @@ #include "hostapd.h" #include "ieee802_11.h" -#include "sta_info.h" #include "wpa.h" #include "mlme.h" @@ -30,6 +29,8 @@ static const char * mlme_auth_alg_str(int alg) return "OPEN_SYSTEM"; case WLAN_AUTH_SHARED_KEY: return "SHARED_KEY"; + case WLAN_AUTH_FT: + return "FT"; } return "unknown"; @@ -57,7 +58,8 @@ void mlme_authenticate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); - mlme_deletekeys_request(hapd, sta); + if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + mlme_deletekeys_request(hapd, sta); } @@ -102,7 +104,8 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - mlme_deletekeys_request(hapd, sta); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); } @@ -127,7 +130,8 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-REASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - mlme_deletekeys_request(hapd, sta); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); } diff --git a/contrib/hostapd/mlme.h b/hostapd/mlme.h index c77a9390a8b63..c77a9390a8b63 100644 --- a/contrib/hostapd/mlme.h +++ b/hostapd/mlme.h diff --git a/hostapd/nt_password_hash.c b/hostapd/nt_password_hash.c new file mode 100644 index 0000000000000..9df307d54a84f --- /dev/null +++ b/hostapd/nt_password_hash.c @@ -0,0 +1,52 @@ +/* + * hostapd - Plaintext password to NtPasswordHash + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ms_funcs.h" + + +int main(int argc, char *argv[]) +{ + unsigned char password_hash[16]; + size_t i; + char *password, buf[64], *pos; + + if (argc > 1) + password = argv[1]; + else { + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("Failed to read password\n"); + return 1; + } + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + password = buf; + } + + nt_password_hash((u8 *) password, strlen(password), password_hash); + for (i = 0; i < sizeof(password_hash); i++) + printf("%02x", password_hash[i]); + printf("\n"); + + return 0; +} diff --git a/hostapd/peerkey.c b/hostapd/peerkey.c new file mode 100644 index 0000000000000..83f3ce513de37 --- /dev/null +++ b/hostapd/peerkey.c @@ -0,0 +1,402 @@ +/* + * hostapd - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "sha1.h" +#include "sha256.h" +#include "wpa.h" +#include "defs.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#ifdef CONFIG_PEERKEY + +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, pos - kde, 0, 0, 0); +} + + +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = os_malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (os_get_random(smk, PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); +#ifdef CONFIG_IEEE80211W + sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#else /* CONFIG_IEEE80211W */ + sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + os_memset(smk, 0, sizeof(*smk)); +} + + +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + os_memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} + + +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + os_free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} + +#endif /* CONFIG_PEERKEY */ diff --git a/contrib/hostapd/pmksa_cache.c b/hostapd/pmksa_cache.c index 0cb85233c8364..5f54a34c11d4b 100644 --- a/contrib/hostapd/pmksa_cache.c +++ b/hostapd/pmksa_cache.c @@ -1,6 +1,6 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,11 +14,13 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "wpa.h" +#include "ap.h" +#include "config.h" +#include "common.h" #include "eloop.h" #include "sha1.h" +#include "sha256.h" #include "ieee802_1x.h" #include "eapol_sm.h" #include "pmksa_cache.h" @@ -45,24 +47,31 @@ struct rsn_pmksa_cache { * @pmk_len: Length of pmk in bytes * @aa: Authenticator address * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * @use_sha256: Whether to use SHA256-based KDF * * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) */ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid) + u8 *pmkid, int use_sha256) { char *title = "PMK Name"; const u8 *addr[3]; const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_MAC_LEN]; + unsigned char hash[SHA256_MAC_LEN]; addr[0] = (u8 *) title; addr[1] = aa; addr[2] = spa; - hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); - memcpy(pmkid, hash, PMKID_LEN); +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + os_memcpy(pmkid, hash, PMKID_LEN); } @@ -73,9 +82,9 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { if (entry == NULL) return; - free(entry->identity); + os_free(entry->identity); ieee802_1x_free_radius_class(&entry->radius_class); - free(entry); + os_free(entry); } @@ -160,11 +169,11 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, return; if (eapol->identity) { - entry->identity = malloc(eapol->identity_len); + entry->identity = os_malloc(eapol->identity_len); if (entry->identity) { entry->identity_len = eapol->identity_len; - memcpy(entry->identity, eapol->identity, - eapol->identity_len); + os_memcpy(entry->identity, eapol->identity, + eapol->identity_len); } } @@ -183,12 +192,12 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, return; if (entry->identity) { - free(eapol->identity); - eapol->identity = malloc(entry->identity_len); + os_free(eapol->identity); + eapol->identity = os_malloc(entry->identity_len); if (eapol->identity) { eapol->identity_len = entry->identity_len; - memcpy(eapol->identity, entry->identity, - entry->identity_len); + os_memcpy(eapol->identity, entry->identity, + entry->identity_len); } wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", eapol->identity, eapol->identity_len); @@ -207,6 +216,37 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, } +static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->spa)); + wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); +} + + /** * pmksa_cache_add - Add a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() @@ -216,6 +256,7 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, * @spa: Supplicant address * @session_timeout: Session timeout * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA @@ -226,28 +267,29 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, int session_timeout, - struct eapol_state_machine *eapol) + struct eapol_state_machine *eapol, int akmp) { - struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct rsn_pmksa_cache_entry *entry, *pos; struct os_time now; if (pmk_len > PMK_LEN) return NULL; - entry = wpa_zalloc(sizeof(*entry)); + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; - memcpy(entry->pmk, pmk, pmk_len); + os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); os_get_time(&now); entry->expiration = now.sec; if (session_timeout > 0) entry->expiration += session_timeout; else entry->expiration += dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->spa, spa, ETH_ALEN); + entry->akmp = akmp; + os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); /* Replace an old entry for the same STA (if found) with the new entry @@ -264,29 +306,44 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, pmksa_cache_free_entry(pmksa, pmksa->pmksa); } - /* Add the new entry; order by expiration time */ - pos = pmksa->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = pmksa->pmksa; - pmksa->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); + entry->pmk_len = old_entry->pmk_len; + entry->expiration = old_entry->expiration; + entry->akmp = old_entry->akmp; + os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); + entry->opportunistic = 1; + if (old_entry->identity) { + entry->identity = os_malloc(old_entry->identity_len); + if (entry->identity) { + entry->identity_len = old_entry->identity_len; + os_memcpy(entry->identity, old_entry->identity, + old_entry->identity_len); + } } - entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; - pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + ieee802_1x_copy_radius_class(&entry->radius_class, + &old_entry->radius_class); + entry->eap_type_authsrv = old_entry->eap_type_authsrv; + entry->vlan_id = old_entry->vlan_id; + entry->opportunistic = 1; - pmksa->pmksa_count++; - wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, - MAC2STR(entry->spa)); - wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); + pmksa_cache_link_entry(pmksa, entry); return entry; } @@ -313,7 +370,7 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); for (i = 0; i < PMKID_HASH_SIZE; i++) pmksa->pmkid[i] = NULL; - free(pmksa); + os_free(pmksa); } @@ -334,9 +391,10 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, else entry = pmksa->pmksa; while (entry) { - if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) return entry; entry = pmkid ? entry->hnext : entry->next; } @@ -345,6 +403,37 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, /** + * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: PMKID + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + * + * Use opportunistic key caching (OKC) to find a PMK for a supplicant. + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, + const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + u8 new_pmkid[PMKID_LEN]; + + entry = pmksa->pmksa; + while (entry) { + if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) + continue; + rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, + wpa_key_mgmt_sha256(entry->akmp)); + if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** * pmksa_cache_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed * @ctx: Context pointer for free_cb function @@ -356,7 +445,7 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, { struct rsn_pmksa_cache *pmksa; - pmksa = wpa_zalloc(sizeof(*pmksa)); + pmksa = os_zalloc(sizeof(*pmksa)); if (pmksa) { pmksa->free_cb = free_cb; pmksa->ctx = ctx; diff --git a/contrib/hostapd/pmksa_cache.h b/hostapd/pmksa_cache.h index dd9074eefa941..6ba2da6073eb0 100644 --- a/contrib/hostapd/pmksa_cache.h +++ b/hostapd/pmksa_cache.h @@ -1,6 +1,6 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -32,6 +32,7 @@ struct rsn_pmksa_cache_entry { struct radius_class_data radius_class; u8 eap_type_authsrv; int vlan_id; + int opportunistic; }; struct rsn_pmksa_cache; @@ -42,13 +43,20 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *pmkid); +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa, + const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, int session_timeout, - struct eapol_state_machine *eapol); + struct eapol_state_machine *eapol, int akmp); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid); void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid); + u8 *pmkid, int use_sha256); #endif /* PMKSA_CACHE_H */ diff --git a/contrib/hostapd/preauth.c b/hostapd/preauth.c index d9928815345af..36af4e31fe950 100644 --- a/contrib/hostapd/preauth.c +++ b/hostapd/preauth.c @@ -1,6 +1,6 @@ /* * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,7 +17,7 @@ #ifdef CONFIG_RSN_PREAUTH #include "hostapd.h" -#include "l2_packet.h" +#include "l2_packet/l2_packet.h" #include "ieee802_1x.h" #include "eloop.h" #include "sta_info.h" @@ -50,29 +50,27 @@ static void rsn_preauth_receive(void *ctx, const u8 *src_addr, struct sta_info *sta; struct l2_ethhdr *ethhdr; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " - "from interface '%s'\n", piface->ifname); + wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " + "from interface '%s'", piface->ifname); if (len < sizeof(*ethhdr) + sizeof(*hdr)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth " - "packet (len=%lu)\n", (unsigned long) len); + wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " + "(len=%lu)", (unsigned long) len); return; } ethhdr = (struct l2_ethhdr *) buf; hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "foreign address " MACSTR "\n", - MAC2STR(ethhdr->h_dest)); + if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " + MACSTR, MAC2STR(ethhdr->h_dest)); return; } sta = ap_get_sta(hapd, ethhdr->h_source); if (sta && (sta->flags & WLAN_STA_ASSOC)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "already association STA " MACSTR "\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " + "STA " MACSTR, MAC2STR(sta->addr)); return; } if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { @@ -103,15 +101,14 @@ static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) { struct rsn_preauth_interface *piface; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", - ifname); + wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); - piface = wpa_zalloc(sizeof(*piface)); + piface = os_zalloc(sizeof(*piface)); if (piface == NULL) return -1; piface->hapd = hapd; - piface->ifname = strdup(ifname); + piface->ifname = os_strdup(ifname); if (piface->ifname == NULL) { goto fail1; } @@ -119,8 +116,8 @@ static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, rsn_preauth_receive, piface, 1); if (piface->l2 == NULL) { - printf("Failed to open register layer 2 access to " - "ETH_P_PREAUTH\n"); + wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " + "to ETH_P_PREAUTH"); goto fail2; } @@ -129,9 +126,9 @@ static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) return 0; fail2: - free(piface->ifname); + os_free(piface->ifname); fail1: - free(piface); + os_free(piface); return -1; } @@ -146,8 +143,8 @@ void rsn_preauth_iface_deinit(struct hostapd_data *hapd) prev = piface; piface = piface->next; l2_packet_deinit(prev->l2); - free(prev->ifname); - free(prev); + os_free(prev->ifname); + os_free(prev); } } @@ -159,7 +156,7 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd) if (hapd->conf->rsn_preauth_interfaces == NULL) return 0; - tmp = strdup(hapd->conf->rsn_preauth_interfaces); + tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); if (tmp == NULL) return -1; start = tmp; @@ -168,7 +165,7 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd) start++; if (*start == '\0') break; - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end) *end = '\0'; @@ -182,7 +179,7 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd) else break; } - free(tmp); + os_free(tmp); return 0; } @@ -200,13 +197,15 @@ static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { - u8 *key; + const u8 *key; size_t len; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_INFO, "pre-authentication %s", success ? "succeeded" : "failed"); - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (len > PMK_LEN) + len = PMK_LEN; if (success && key) { if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, sta->addr, @@ -245,26 +244,26 @@ void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, } if (piface == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " - "pre-authentication interface for " MACSTR "\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " + "interface for " MACSTR, MAC2STR(sta->addr)); return; } - ethhdr = malloc(sizeof(*ethhdr) + len); + ethhdr = os_malloc(sizeof(*ethhdr) + len); if (ethhdr == NULL) return; - memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); - memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); + os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); + os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); ethhdr->h_proto = htons(ETH_P_PREAUTH); - memcpy(ethhdr + 1, buf, len); + os_memcpy(ethhdr + 1, buf, len); if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, sizeof(*ethhdr) + len) < 0) { - printf("Failed to send preauth packet using l2_packet_send\n"); + wpa_printf(MSG_ERROR, "Failed to send preauth packet using " + "l2_packet_send\n"); } - free(ethhdr); + os_free(ethhdr); } diff --git a/contrib/hostapd/preauth.h b/hostapd/preauth.h index 5348bee9bf72e..5348bee9bf72e 100644 --- a/contrib/hostapd/preauth.h +++ b/hostapd/preauth.h diff --git a/hostapd/prism54.h b/hostapd/prism54.h new file mode 100644 index 0000000000000..cb0a9a19ba8b5 --- /dev/null +++ b/hostapd/prism54.h @@ -0,0 +1,177 @@ +#ifndef PRISM54_H +#define PRISM54_H + +struct ieee802_3_hdr_s { + unsigned char da[6]; + unsigned char sa[6]; + unsigned short type; +} __attribute__ ((packed)); + +typedef struct ieee802_3_hdr_s ieee802_3_hdr; + +#define PIMOP_GET 0 +#define PIMOP_SET 1 +#define PIMOP_RESPONSE 2 +#define PIMOP_ERROR 3 +#define PIMOP_TRAP 4 + +struct pimdev_hdr_s { + int op; + unsigned long oid; +} __attribute__ ((packed)); + +typedef struct pimdev_hdr_s pimdev_hdr; + +#define DOT11_OID_ATTACHMENT 0x19000003 + +/* really need to check */ +#define DOT11_PKT_BEACON 0x80 +#define DOT11_PKT_ASSOC_RESP 0x10 +#define DOT11_PKT_REASSOC_RESP 0x30 +#define DOT11_PKT_PROBE_RESP 0x50 + +struct obj_attachment_hdr { + char type; + char reserved; + short id; + short size; +} __attribute__ ((packed)); + +struct obj_attachment { + char type; + char reserved; + short id; + short size; + char data[1]; +} __attribute__ ((packed)); + +#define DOT11_OID_MLMEAUTOLEVEL 0x19000001 +#define DOT11_MLME_AUTO 0 +#define DOT11_MLME_INTERMEDIATE 0x01000000 +#define DOT11_MLME_EXTENDED 0x02000000 + +#define DOT11_OID_DEAUTHENTICATE 0x18000000 +#define DOT11_OID_AUTHENTICATE 0x18000001 +#define DOT11_OID_DISASSOCIATE 0x18000002 +#define DOT11_OID_ASSOCIATE 0x18000003 +#define DOT11_OID_BEACON 0x18000005 +#define DOT11_OID_PROBE 0x18000006 +#define DOT11_OID_REASSOCIATE 0x1800000b + +struct obj_mlme { + char address[6]; + short id; + short state; + short code; +} __attribute__ ((packed)); + +#define DOT11_OID_DEAUTHENTICATEEX 0x18000007 +#define DOT11_OID_AUTHENTICATEEX 0x18000008 +#define DOT11_OID_DISASSOCIATEEX 0x18000009 +#define DOT11_OID_ASSOCIATEEX 0x1800000a +#define DOT11_OID_REASSOCIATEEX 0x1800000c + +struct obj_mlmeex { + char address[6]; + short id; + short state; + short code; + short size; + char data[1]; +} __attribute__ ((packed)); + +#define DOT11_OID_STAKEY 0x12000008 + +#define DOT11_PRIV_WEP 0 +#define DOT11_PRIV_TKIP 1 + +/* endian reversed to bigger endian */ +#define DOT11_STAKEY_OPTION_DEFAULTKEY 0x100 + +struct obj_stakey { + char address[6]; + char keyid; + char reserved; + short options; + char type; + char length; + char key[32]; +} __attribute__ ((packed)); + +#define DOT11_OID_DEFKEYID 0x12000003 +#define DOT11_OID_DEFKEY1 0x12000004 +#define DOT11_OID_DEFKEY2 0x12000005 +#define DOT11_OID_DEFKEY3 0x12000006 +#define DOT11_OID_DEFKEY4 0x12000007 + +struct obj_key { + char type; + char length; + char key[32]; +} __attribute__ ((packed)); + +#define DOT11_OID_STASC 0x1200000a + +struct obj_stasc { + char address[6]; + char keyid; + char tx_sc; + unsigned long sc_high; + unsigned short sc_low; +} __attribute__ ((packed)); + +#define DOT11_OID_CLIENTS 0x15000001 +#define DOT11_OID_CLIENTSASSOCIATED 0x15000002 +#define DOT11_OID_CLIENTST 0x15000003 +#define DOT11_OID_CLIENTEND 0x150007d9 +#define DOT11_OID_CLIENTFIND 0x150007db + +#define DOT11_NODE_UNKNOWN +#define DOT11_NODE_CLIENT +#define DOT11_NODE_AP + +/* endian reversed to bigger endian */ +#define DOT11_STATE_NONE 0 +#define DOT11_STATE_AUTHING 0x100 +#define DOT11_STATE_AUTH 0x200 +#define DOT11_STATE_ASSOCING 0x300 +#define DOT11_STATE_REASSOCING 0x400 +#define DOT11_STATE_ASSOC 0x500 +#define DOT11_STATE_WDS 0x600 + +struct obj_sta { + char address[6]; + char pad[2]; + char state; + char node; + short age; + char reserved1; + char rssi; + char rate; + char reserved2; +} __attribute__ ((packed)); + +#define DOT11_OID_SSID 0x10000002 +#define DOT11_OID_SSIDOVERRIDE 0x10000006 + +struct obj_ssid { + char length; + char octets[33]; +} __attribute__ ((packed)); + +#define DOT11_OID_EAPAUTHSTA 0x150007de +#define DOT11_OID_EAPUNAUTHSTA 0x150007df +/* not in 38801 datasheet??? */ +#define DOT11_OID_DOT1XENABLE 0x150007e0 +#define DOT11_OID_MICFAILURE 0x150007e1 +#define DOT11_OID_AUTHENABLE 0x12000000 +#define DOT11_OID_PRIVACYINVOKED 0x12000001 +#define DOT11_OID_EXUNENCRYPTED 0x12000002 + +#define DOT11_AUTH_OS 0x01000000 +#define DOT11_AUTH_SK 0x02000000 +#define DOT11_AUTH_BOTH 0x03000000 + +#define DOT11_BOOL_TRUE 0x01000000 + +#endif /* PRISM54_H */ diff --git a/hostapd/priv_netlink.h b/hostapd/priv_netlink.h new file mode 100644 index 0000000000000..d1f6f663ebf7c --- /dev/null +++ b/hostapd/priv_netlink.h @@ -0,0 +1,71 @@ +#ifndef PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* Private copy of needed Linux netlink/rtnetlink definitions. + * + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/hostapd/radiotap.c b/hostapd/radiotap.c new file mode 100644 index 0000000000000..804473fa4bfb9 --- /dev/null +++ b/hostapd/radiotap.c @@ -0,0 +1,287 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * + * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> + * I only modified some things on top to ease syncing should bugs be found. + */ + +#include "includes.h" + +#include "common.h" +#include "radiotap_iter.h" + +#define le16_to_cpu le_to_host16 +#define le32_to_cpu le_to_host32 +#define __le32 uint32_t +#define ulong unsigned long +#define unlikely(cond) (cond) +#define get_unaligned(p) \ +({ \ + struct packed_dummy_struct { \ + typeof(*(p)) __val; \ + } __attribute__((packed)) *__ptr = (void *) (p); \ + \ + __ptr->__val; \ +}) + +/* function prototypes and related defs are in radiotap_iter.h */ + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header. It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code. Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member. This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length) +{ + /* Linux only supports version 0 radiotap format */ + if (radiotap_header->it_version) + return -EINVAL; + + /* sanity check for allowed length and radiotap length field */ + if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) + return -EINVAL; + + iterator->rtheader = radiotap_header; + iterator->max_length = le16_to_cpu(get_unaligned( + &radiotap_header->it_len)); + iterator->arg_index = 0; + iterator->bitmap_shifter = le32_to_cpu(get_unaligned( + &radiotap_header->it_present)); + iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); + iterator->this_arg = NULL; + + /* find payload start allowing for extended bitmap(s) */ + + if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { + while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & + (1<<IEEE80211_RADIOTAP_EXT)) { + iterator->arg += sizeof(u32); + + /* + * check for insanity where the present bitmaps + * keep claiming to extend up to or even beyond the + * stated radiotap header length + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) + > (ulong)iterator->max_length) + return -EINVAL; + } + + iterator->arg += sizeof(u32); + + /* + * no need to check again for blowing past stated radiotap + * header length, because ieee80211_radiotap_iterator_next + * checks it before it is dereferenced + */ + } + + /* we are all initialized happily */ + + return 0; +} + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field. It takes care of alignment handling and extended + * present fields. @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator) +{ + + /* + * small length lookup table for all radiotap types we heard of + * starting from b0 in the bitmap, so we can walk the payload + * area of the radiotap header + * + * There is a requirement to pad args, so that args + * of a given length must begin at a boundary of that length + * -- but note that compound args are allowed (eg, 2 x u16 + * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not + * a reliable indicator of alignment requirement. + * + * upper nybble: content alignment for arg + * lower nybble: content length for arg + */ + + static const u8 rt_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = 0x88, + [IEEE80211_RADIOTAP_FLAGS] = 0x11, + [IEEE80211_RADIOTAP_RATE] = 0x11, + [IEEE80211_RADIOTAP_CHANNEL] = 0x24, + [IEEE80211_RADIOTAP_FHSS] = 0x22, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, + [IEEE80211_RADIOTAP_ANTENNA] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, + [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, + /* + * add more here as they are defined in + * include/net/ieee80211_radiotap.h + */ + }; + + /* + * for every radiotap entry we can at + * least skip (by knowing the length)... + */ + + while (iterator->arg_index < (int) sizeof(rt_sizes)) { + int hit = 0; + int pad; + + if (!(iterator->bitmap_shifter & 1)) + goto next_entry; /* arg not present */ + + /* + * arg is present, account for alignment padding + * 8-bit args can be at any alignment + * 16-bit args must start on 16-bit boundary + * 32-bit args must start on 32-bit boundary + * 64-bit args must start on 64-bit boundary + * + * note that total arg size can differ from alignment of + * elements inside arg, so we use upper nybble of length + * table to base alignment on + * + * also note: these alignments are ** relative to the + * start of the radiotap header **. There is no guarantee + * that the radiotap header itself is aligned on any + * kind of boundary. + * + * the above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area + */ + + pad = (((ulong)iterator->arg) - + ((ulong)iterator->rtheader)) & + ((rt_sizes[iterator->arg_index] >> 4) - 1); + + if (pad) + iterator->arg += + (rt_sizes[iterator->arg_index] >> 4) - pad; + + /* + * this is what we will return to user, but we need to + * move on first so next call has something fresh to test + */ + iterator->this_arg_index = iterator->arg_index; + iterator->this_arg = iterator->arg; + hit = 1; + + /* internally move on the size of this arg */ + iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + + /* + * check for insanity where we are given a bitmap that + * claims to have more arg content than the length of the + * radiotap section. We will normally end up equalling this + * max_length on the last arg, never exceeding it. + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) > + (ulong) iterator->max_length) + return -EINVAL; + + next_entry: + iterator->arg_index++; + if (unlikely((iterator->arg_index & 31) == 0)) { + /* completed current u32 bitmap */ + if (iterator->bitmap_shifter & 1) { + /* b31 was set, there is more */ + /* move to next u32 bitmap */ + iterator->bitmap_shifter = le32_to_cpu( + get_unaligned(iterator->next_bitmap)); + iterator->next_bitmap++; + } else + /* no more bitmaps: end */ + iterator->arg_index = sizeof(rt_sizes); + } else /* just try the next bit */ + iterator->bitmap_shifter >>= 1; + + /* if we found a valid arg earlier, return it now */ + if (hit) + return 0; + } + + /* we don't know how to handle any more args, we're done */ + return -ENOENT; +} diff --git a/hostapd/radiotap.h b/hostapd/radiotap.h new file mode 100644 index 0000000000000..508264c4cf333 --- /dev/null +++ b/hostapd/radiotap.h @@ -0,0 +1,242 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include <stdint.h> + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { + uint8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + uint8_t it_pad; + uint16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + uint32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +}; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS uint16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/hostapd/radiotap_iter.h b/hostapd/radiotap_iter.h new file mode 100644 index 0000000000000..92a798a67023a --- /dev/null +++ b/hostapd/radiotap_iter.h @@ -0,0 +1,41 @@ +#ifndef __RADIOTAP_ITER_H +#define __RADIOTAP_ITER_H + +#include "radiotap.h" + +/* Radiotap header iteration + * implemented in radiotap.c + */ +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @rtheader: pointer to the radiotap header we are walking through + * @max_length: length of radiotap header in cpu byte ordering + * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg + * @this_arg: pointer to current radiotap arg + * @arg_index: internal next argument index + * @arg: internal next argument pointer + * @next_bitmap: internal pointer to next present u32 + * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + */ + +struct ieee80211_radiotap_iterator { + struct ieee80211_radiotap_header *rtheader; + int max_length; + int this_arg_index; + unsigned char *this_arg; + + int arg_index; + unsigned char *arg; + uint32_t *next_bitmap; + uint32_t bitmap_shifter; +}; + +extern int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length); + +extern int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator); + +#endif /* __RADIOTAP_ITER_H */ diff --git a/contrib/hostapd/sta_info.c b/hostapd/sta_info.c index dbb7f6cef7198..a139ba9bdf94c 100644 --- a/contrib/hostapd/sta_info.c +++ b/hostapd/sta_info.c @@ -1,6 +1,7 @@ /* * hostapd / Station table - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,11 +21,10 @@ #include "accounting.h" #include "ieee802_1x.h" #include "ieee802_11.h" -#include "radius.h" -#include "eapol_sm.h" +#include "radius/radius.h" #include "wpa.h" #include "preauth.h" -#include "radius_client.h" +#include "radius/radius_client.h" #include "driver.h" #include "beacon.h" #include "hw_features.h" @@ -34,6 +34,9 @@ static int ap_sta_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta, u32 flags); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_IEEE80211W */ int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -56,7 +59,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) struct sta_info *s; s = hapd->sta_hash[STA_HASH(sta)]; - while (s != NULL && memcmp(s->addr, sta, 6) != 0) + while (s != NULL && os_memcmp(s->addr, sta, 6) != 0) s = s->hnext; return s; } @@ -75,8 +78,8 @@ static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) while (tmp != NULL && tmp->next != sta) tmp = tmp->next; if (tmp == NULL) { - printf("Could not remove STA " MACSTR " from list.\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " + "list.", MAC2STR(sta->addr)); } else tmp->next = sta->next; } @@ -95,18 +98,19 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) s = hapd->sta_hash[STA_HASH(sta->addr)]; if (s == NULL) return; - if (memcmp(s->addr, sta->addr, 6) == 0) { + if (os_memcmp(s->addr, sta->addr, 6) == 0) { hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; return; } - while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0) + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; else - printf("AP: could not remove STA " MACSTR " from hash table\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR + " from hash table", MAC2STR(sta->addr)); } @@ -150,6 +154,26 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) set_beacon++; } +#ifdef CONFIG_IEEE80211N + if (sta->no_ht_gf_set) { + sta->no_ht_gf_set = 0; + hapd->iface->num_sta_ht_no_gf--; + } + + if (sta->no_ht_set) { + sta->no_ht_set = 0; + hapd->iface->num_sta_no_ht--; + } + + if (sta->ht_20mhz_set) { + sta->ht_20mhz_set = 0; + hapd->iface->num_sta_ht_20mhz--; + } + + if (hostapd_ht_operation_update(hapd->iface) > 0) + set_beacon++; +#endif /* CONFIG_IEEE80211N */ + if (set_beacon) ieee802_11_set_beacons(hapd->iface); @@ -161,12 +185,17 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) rsn_preauth_free_station(hapd, sta); radius_client_flush_auth(hapd->radius, sta->addr); - if (sta->last_assoc_req) - free(sta->last_assoc_req); + os_free(sta->last_assoc_req); + os_free(sta->challenge); + +#ifdef CONFIG_IEEE80211W + os_free(sta->sa_query_trans_id); + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); +#endif /* CONFIG_IEEE80211W */ - free(sta->challenge); + wpabuf_free(sta->wps_ie); - free(sta); + os_free(sta); } @@ -183,12 +212,21 @@ void hostapd_free_stas(struct hostapd_data *hapd) hapd, sta, WLAN_REASON_UNSPECIFIED); } sta = sta->next; - printf("Removing station " MACSTR "\n", MAC2STR(prev->addr)); + wpa_printf(MSG_DEBUG, "Removing station " MACSTR, + MAC2STR(prev->addr)); ap_free_sta(hapd, prev); } } +/** + * ap_handle_timer - Per STA timer handler + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: struct sta_info * + * + * This function is called to check station activity and to remove inactive + * stations. + */ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; @@ -207,19 +245,17 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Checking STA " MACSTR " inactivity:\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", + MAC2STR(sta->addr)); inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); if (inactive_sec == -1) { - printf(" Could not get station info from kernel " - "driver for " MACSTR ".\n", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Could not get station info " + "from kernel driver for " MACSTR ".", + MAC2STR(sta->addr)); } else if (inactive_sec < hapd->conf->ap_max_inactivity && sta->flags & WLAN_STA_ASSOC) { /* station activity detected; reset timeout state */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Station has been active\n"); + wpa_printf(MSG_DEBUG, " Station has been active"); sta->timeout_next = STA_NULLFUNC; next_time = hapd->conf->ap_max_inactivity - inactive_sec; @@ -229,8 +265,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if ((sta->flags & WLAN_STA_ASSOC) && sta->timeout_next == STA_DISASSOC && !(sta->flags & WLAN_STA_PENDING_POLL)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Station has ACKed data poll\n"); + wpa_printf(MSG_DEBUG, " Station has ACKed data poll"); /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; @@ -249,22 +284,32 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) * is ACKed */ struct ieee80211_hdr hdr; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Polling STA with data frame\n"); + wpa_printf(MSG_DEBUG, " Polling STA with data frame"); sta->flags |= WLAN_STA_PENDING_POLL; #ifndef CONFIG_NATIVE_WINDOWS - /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but - * it is apparently not retried so TX Exc events are not - * received for it */ - memset(&hdr, 0, sizeof(hdr)); - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); - hdr.frame_control |= host_to_le16(BIT(1)); + os_memset(&hdr, 0, sizeof(hdr)); + if (hapd->driver && + os_strcmp(hapd->driver->name, "hostap") == 0) { + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + */ + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + } else { + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + } + hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); - memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN); - memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, + ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) perror("ap_handle_timer: send"); @@ -272,9 +317,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; - printf(" Sending %s info to STA " MACSTR "\n", - deauth ? "deauthentication" : "disassociation", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); if (deauth) { hostapd_sta_deauth(hapd, sta->addr, @@ -342,7 +387,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) "session timeout"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; - memcpy(addr, sta->addr, ETH_ALEN); + os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -374,17 +419,17 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) if (sta) return sta; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n"); + wpa_printf(MSG_DEBUG, " New STA"); if (hapd->num_sta >= hapd->conf->max_num_sta) { /* FIX: might try to remove some old STAs first? */ - printf(" no more room for new STAs (%d/%d)\n", - hapd->num_sta, hapd->conf->max_num_sta); + wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", + hapd->num_sta, hapd->conf->max_num_sta); return NULL; } - sta = wpa_zalloc(sizeof(struct sta_info)); + sta = os_zalloc(sizeof(struct sta_info)); if (sta == NULL) { - printf(" malloc failed\n"); + wpa_printf(MSG_ERROR, "malloc failed"); return NULL; } sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; @@ -392,7 +437,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) /* initialize STA info data */ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, ap_handle_timer, hapd, sta); - memcpy(sta->addr, addr, ETH_ALEN); + os_memcpy(sta->addr, addr, ETH_ALEN); sta->next = hapd->sta_list; hapd->sta_list = sta; hapd->num_sta++; @@ -407,12 +452,12 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) { ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Removing STA " MACSTR - " from kernel driver\n", MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", + MAC2STR(sta->addr)); if (hostapd_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { - printf("Could not remove station " MACSTR " from kernel " - "driver.\n", MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR + " from kernel driver.", MAC2STR(sta->addr)); return -1; } return 0; @@ -446,8 +491,8 @@ static int ap_sta_in_other_bss(struct hostapd_data *hapd, void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "%s: disassociate STA " MACSTR - "\n", hapd->conf->iface, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_ASSOC; if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) ap_sta_remove(hapd, sta); @@ -465,8 +510,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "%s: deauthenticate STA " MACSTR - "\n", hapd->conf->iface, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) ap_sta_remove(hapd, sta); @@ -583,3 +628,84 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); } + + +#ifdef CONFIG_IEEE80211W + +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout < tu) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query timed out"); + sta->sa_query_timed_out = 1; + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + return 1; + } + + return 0; +} + + +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (sta->sa_query_count > 0 && + ap_check_sa_query_timeout(hapd, sta)) + return; + + nbuf = os_realloc(sta->sa_query_trans_id, + (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (sta->sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&sta->sa_query_start); + } + trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + sta->sa_query_trans_id = nbuf; + sta->sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = hapd->conf->assoc_sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query attempt %d", sta->sa_query_count); + + ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); +} + + +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + ap_sa_query_timer(hapd, sta); +} + + +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; +} + +#endif /* CONFIG_IEEE80211W */ diff --git a/contrib/hostapd/sta_info.h b/hostapd/sta_info.h index 1d9ab9687ab87..e835970923819 100644 --- a/contrib/hostapd/sta_info.h +++ b/hostapd/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -36,5 +36,8 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid); +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/contrib/hostapd/vlan_init.c b/hostapd/vlan_init.c index d546c74afdc69..87c61e2c01155 100644 --- a/contrib/hostapd/vlan_init.c +++ b/hostapd/vlan_init.c @@ -26,12 +26,6 @@ #include <sys/ioctl.h> #include <linux/sockios.h> #include <linux/if_vlan.h> -typedef __uint64_t __u64; -typedef __uint32_t __u32; -typedef __int32_t __s32; -typedef __uint16_t __u16; -typedef __int16_t __s16; -typedef __uint8_t __u8; #include <linux/if_bridge.h> #include "priv_netlink.h" @@ -53,8 +47,8 @@ static int ifconfig_helper(const char *if_name, int up) return -1; } - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { perror("ioctl[SIOCGIFFLAGS]"); @@ -127,7 +121,7 @@ static int br_delif(const char *br_name, const char *if_name) args[0] = BRCTL_DEL_IF; args[1] = if_index; - strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) args; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { @@ -173,7 +167,7 @@ static int br_addif(const char *br_name, const char *if_name) args[0] = BRCTL_ADD_IF; args[1] = if_index; - strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) args; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { @@ -274,8 +268,8 @@ static int br_getnumports(const char *br_name) arg[2] = MAX_BR_PORTS; arg[3] = 0; - memset(ifindices, 0, sizeof(ifindices)); - strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); ifr.ifr_data = (__caddr_t) arg; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { @@ -300,7 +294,7 @@ static int vlan_rem(const char *if_name) int fd; struct vlan_ioctl_args if_request; - if ((strlen(if_name) + 1) > sizeof(if_request.device1)) { + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { fprintf(stderr, "Interface name to long.\n"); return -1; } @@ -310,9 +304,9 @@ static int vlan_rem(const char *if_name) return -1; } - memset(&if_request, 0, sizeof(if_request)); + os_memset(&if_request, 0, sizeof(if_request)); - strcpy(if_request.device1, if_name); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); if_request.cmd = DEL_VLAN_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { @@ -341,7 +335,7 @@ static int vlan_add(const char *if_name, int vid) ifconfig_up(if_name); - if ((strlen(if_name) + 1) > sizeof(if_request.device1)) { + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { fprintf(stderr, "Interface name to long.\n"); return -1; } @@ -351,12 +345,12 @@ static int vlan_add(const char *if_name, int vid) return -1; } - memset(&if_request, 0, sizeof(if_request)); + os_memset(&if_request, 0, sizeof(if_request)); /* Determine if a suitable vlan device already exists. */ - snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", - vid); + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); if_request.cmd = _GET_VLAN_VID_CMD; @@ -365,8 +359,8 @@ static int vlan_add(const char *if_name, int vid) if (if_request.u.VID == vid) { if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 - && strncmp(if_request.u.device2, if_name, + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, sizeof(if_request.u.device2)) == 0) { close(fd); return 1; @@ -376,8 +370,8 @@ static int vlan_add(const char *if_name, int vid) /* A suitable vlan device does not already exist, add one. */ - memset(&if_request, 0, sizeof(if_request)); - strcpy(if_request.device1, if_name); + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); if_request.u.VID = vid; if_request.cmd = ADD_VLAN_CMD; @@ -402,7 +396,7 @@ static int vlan_set_name_type(unsigned int name_type) return -1; } - memset(&if_request, 0, sizeof(if_request)); + os_memset(&if_request, 0, sizeof(if_request)); if_request.u.name_type = name_type; if_request.cmd = SET_VLAN_NAME_TYPE_CMD; @@ -425,10 +419,10 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; while (vlan) { - if (strcmp(ifname, vlan->ifname) == 0) { + if (os_strcmp(ifname, vlan->ifname) == 0) { - snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); if (!br_addbr(br_name)) vlan->clean |= DVLAN_CLEAN_BR; @@ -440,8 +434,8 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) if (!vlan_add(tagged_interface, vlan->vlan_id)) vlan->clean |= DVLAN_CLEAN_VLAN; - snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); if (!br_addif(br_name, vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; @@ -472,13 +466,13 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) first = prev = vlan; while (vlan) { - if (strcmp(ifname, vlan->ifname) == 0) { - snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + if (os_strcmp(ifname, vlan->ifname) == 0) { + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); if (tagged_interface) { - snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); numports = br_getnumports(br_name); if (numports == 1) { @@ -496,7 +490,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) } else { prev->next = vlan->next; } - free(vlan); + os_free(vlan); break; } @@ -536,11 +530,11 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, if (n < 0) break; - memset(ifname, 0, sizeof(ifname)); + os_memset(ifname, 0, sizeof(ifname)); if ((size_t) n > sizeof(ifname)) n = sizeof(ifname); - memcpy(ifname, ((char *) attr) + rta_len, n); + os_memcpy(ifname, ((char *) attr) + rta_len, n); if (del) vlan_dellink(ifname, hapd); @@ -610,36 +604,33 @@ full_dynamic_vlan_init(struct hostapd_data *hapd) struct sockaddr_nl local; struct full_dynamic_vlan *priv; - priv = malloc(sizeof(*priv)); - + priv = os_zalloc(sizeof(*priv)); if (priv == NULL) return NULL; - memset(priv, 0, sizeof(*priv)); - vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (priv->s < 0) { perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - free(priv); + os_free(priv); return NULL; } - memset(&local, 0, sizeof(local)); + os_memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK; if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { perror("bind(netlink)"); close(priv->s); - free(priv); + os_free(priv); return NULL; } if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) { close(priv->s); - free(priv); + os_free(priv); return NULL; } @@ -653,7 +644,7 @@ static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) return; eloop_unregister_read_sock(priv->s); close(priv->s); - free(priv); + os_free(priv); } #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -774,31 +765,31 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; - ifname = strdup(vlan->ifname); + ifname = os_strdup(vlan->ifname); if (ifname == NULL) return NULL; - pos = strchr(ifname, '#'); + pos = os_strchr(ifname, '#'); if (pos == NULL) { - free(ifname); + os_free(ifname); return NULL; } *pos++ = '\0'; - n = malloc(sizeof(*n)); + n = os_zalloc(sizeof(*n)); if (n == NULL) { - free(ifname); + os_free(ifname); return NULL; } - memset(n, 0, sizeof(*n)); n->vlan_id = vlan_id; n->dynamic_vlan = 1; - snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); - free(ifname); + os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, + pos); + os_free(ifname); if (hostapd_if_add(hapd, HOSTAPD_IF_VLAN, n->ifname, NULL)) { - free(n); + os_free(n); return NULL; } diff --git a/contrib/hostapd/vlan_init.h b/hostapd/vlan_init.h index cf55ac2462209..cf55ac2462209 100644 --- a/contrib/hostapd/vlan_init.h +++ b/hostapd/vlan_init.h diff --git a/contrib/hostapd/wired.conf b/hostapd/wired.conf index 956f8c53c540f..956f8c53c540f 100644 --- a/contrib/hostapd/wired.conf +++ b/hostapd/wired.conf diff --git a/contrib/hostapd/wme.c b/hostapd/wme.c index 8be740f7e468d..727ee7ef50a31 100644 --- a/contrib/hostapd/wme.c +++ b/hostapd/wme.c @@ -81,21 +81,22 @@ int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len); if (len < sizeof(struct wme_information_element)) { - printf("Too short WME IE (len=%lu)\n", (unsigned long) len); + wpa_printf(MSG_DEBUG, "Too short WME IE (len=%lu)", + (unsigned long) len); return -1; } wme = (struct wme_information_element *) eid; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Validating WME IE: OUI " - "%02x:%02x:%02x OUI type %d OUI sub-type %d " - "version %d\n", - wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type, - wme->oui_subtype, wme->version); - if (memcmp(wme->oui, wme_oui, sizeof(wme_oui)) != 0 || + wpa_printf(MSG_DEBUG, "Validating WME IE: OUI %02x:%02x:%02x " + "OUI type %d OUI sub-type %d version %d", + wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type, + wme->oui_subtype, wme->version); + if (os_memcmp(wme->oui, wme_oui, sizeof(wme_oui)) != 0 || wme->oui_type != WME_OUI_TYPE || wme->oui_subtype != WME_OUI_SUBTYPE_INFORMATION_ELEMENT || wme->version != WME_VERSION) { - printf("Unsupported WME IE OUI/Type/Subtype/Version\n"); + wpa_printf(MSG_DEBUG, "Unsupported WME IE OUI/Type/Subtype/" + "Version"); return -1; } @@ -110,9 +111,11 @@ int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta) { /* update kernel STA data for WME related items (WLAN_STA_WPA flag) */ if (sta->flags & WLAN_STA_WME) - hostapd_sta_set_flags(hapd, sta->addr, WLAN_STA_WME, ~0); + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + WLAN_STA_WME, ~0); else - hostapd_sta_set_flags(hapd, sta->addr, 0, ~WLAN_STA_WME); + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + 0, ~WLAN_STA_WME); return 0; } @@ -132,17 +135,17 @@ static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "action response - reason %d", status_code); - memset(buf, 0, sizeof(buf)); + os_memset(buf, 0, sizeof(buf)); m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); - memcpy(m->da, addr, ETH_ALEN); - memcpy(m->sa, hapd->own_addr, ETH_ALEN); - memcpy(m->bssid, hapd->own_addr, ETH_ALEN); - m->u.action.category = WME_ACTION_CATEGORY; + os_memcpy(m->da, addr, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + m->u.action.category = WLAN_ACTION_WMM; m->u.action.u.wme_action.action_code = action_code; m->u.action.u.wme_action.dialog_token = dialogue_token; m->u.action.u.wme_action.status_code = status_code; - memcpy(t, tspec, sizeof(struct wme_tspec_info_element)); + os_memcpy(t, tspec, sizeof(struct wme_tspec_info_element)); len = ((u8 *) (t + 1)) - buf; if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0) @@ -209,8 +212,7 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, } /* extract the tspec info element */ - if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed) - { + if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_wme_action - could not parse wme " diff --git a/contrib/hostapd/wme.h b/hostapd/wme.h index 8c208832e5258..4ee281ab7d2c9 100644 --- a/contrib/hostapd/wme.h +++ b/hostapd/wme.h @@ -26,24 +26,6 @@ #endif /* defined(__FreeBSD__) || defined(__NetBSD__) || * defined(__DragonFly__) */ -#define WME_OUI_TYPE 2 -#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0 -#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1 -#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2 -#define WME_VERSION 1 - -#define WME_ACTION_CATEGORY 17 -#define WME_ACTION_CODE_SETUP_REQUEST 0 -#define WME_ACTION_CODE_SETUP_RESPONSE 1 -#define WME_ACTION_CODE_TEARDOWN 2 - -#define WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED 0 -#define WME_SETUP_RESPONSE_STATUS_INVALID_PARAMETERS 1 -#define WME_SETUP_RESPONSE_STATUS_REFUSED 3 - -#define WME_TSPEC_DIRECTION_UPLINK 0 -#define WME_TSPEC_DIRECTION_DOWNLINK 1 -#define WME_TSPEC_DIRECTION_BI_DIRECTIONAL 3 extern inline u16 tsinfo(int tag1d, int contention_based, int direction) { @@ -88,7 +70,7 @@ struct wme_ac_parameter { #endif /* bytes 3 & 4 */ - u16 txopLimit; + le16 txopLimit; } __attribute__ ((packed)); struct wme_parameter_element { @@ -136,6 +118,7 @@ enum { WME_AC_VO = 3 }; +struct ieee80211_mgmt; u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid); int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len); diff --git a/hostapd/wpa.c b/hostapd/wpa.c new file mode 100644 index 0000000000000..cf285b6fd6fea --- /dev/null +++ b/hostapd/wpa.c @@ -0,0 +1,2447 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "common.h" +#include "config.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "sha1.h" +#include "sha256.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "pmksa_cache.h" +#include "state_machine.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_request_new_ptk(struct wpa_state_machine *sm); + +static const u32 dot11RSNAConfigGroupUpdateCount = 4; +static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; +static const u32 eapol_key_timeout_first = 100; /* ms */ +static const u32 eapol_key_timeout_subseq = 1000; /* ms */ + +/* TODO: make these configurable */ +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; + + +static inline void wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *prev_psk) +{ + if (wpa_auth->cb.get_psk == NULL) + return NULL; + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); +} + + +static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len) +{ + if (wpa_auth->cb.get_msk == NULL) + return -1; + return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_auth == NULL) + return 0; + return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + os_free(format); +} + + +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +static int wpa_use_aes_cmac(struct wpa_state_machine *sm) +{ + int ret = 0; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211W */ + return ret; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + + if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + } + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; + + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; + do { + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); + wpa_request_new_ptk(sm); + wpa_sm_step(sm); +} + + +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->pmksa == ctx) + sm->pmksa = NULL; + return 0; +} + + +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) +{ + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +} + + +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + group = os_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; + + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; + + switch (wpa_auth->conf.wpa_group) { + case WPA_CIPHER_CCMP: + group->GTK_len = 16; + break; + case WPA_CIPHER_TKIP: + group->GTK_len = 32; + break; + case WPA_CIPHER_WEP104: + group->GTK_len = 13; + break; + case WPA_CIPHER_WEP40: + group->GTK_len = 5; + break; + } + + /* Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (os_get_random(rkey, sizeof(rkey)) || + os_get_random(group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + os_free(group); + return NULL; + } + + sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN); + + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * @cb: Callback functions for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->group = wpa_group_init(wpa_auth, 0); + if (wpa_auth->group == NULL) { + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +#ifdef CONFIG_IEEE80211R + wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); + if (wpa_auth->ft_pmk_cache == NULL) { + wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->wpa_ie); + pmksa_cache_deinit(wpa_auth->pmksa); + os_free(wpa_auth); + return NULL; + } +#endif /* CONFIG_IEEE80211R */ + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; +} + + +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *prev; + + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + +#ifdef CONFIG_PEERKEY + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); +#endif /* CONFIG_PEERKEY */ + + pmksa_cache_deinit(wpa_auth->pmksa); + +#ifdef CONFIG_IEEE80211R + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_auth->wpa_ie); + + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + os_free(prev); + } + + os_free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + if (wpa_auth == NULL) + return 0; + + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + return -1; + } + + return 0; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_state_machine *sm; + + sm = os_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + os_memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + + return sm; +} + + +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; + +#ifdef CONFIG_IEEE80211R + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " + "start 4-way handshake"); + return; + } +#endif /* CONFIG_IEEE80211R */ + + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; + wpa_sm_step(sm); + return; + } + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; + + sm->Init = TRUE; + wpa_sm_step(sm); + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + wpa_sm_step(sm); +} + + +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) +{ + /* WPA/RSN was not used - clear WPA state. This is needed if the STA + * reassociates back to the same AP while the previous entry for the + * STA has not yet been removed. */ + if (sm == NULL) + return; + + sm->wpa_key_mgmt = 0; +} + + +static void wpa_free_sta_sm(struct wpa_state_machine *sm) +{ + os_free(sm->last_rx_eapol_key); + os_free(sm->wpa_ie); + os_free(sm); +} + + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); +} + + +static void wpa_request_new_ptk(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +static int wpa_replay_counter_valid(struct wpa_state_machine *sm, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + if (os_memcmp(replay_counter, sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) + return 1; + } + return 0; +} + + +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; + char *msgtxt; + struct wpa_eapol_ie_parse kde; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + key_data_length = WPA_GET_BE16(key->key_data_length); + if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { + wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %lu)", + key_data_length, + (unsigned long) (data_len - sizeof(*hdr) - + sizeof(*key))); + return; + } + + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys + * are set */ + + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (sm->pairwise == WPA_CIPHER_CCMP) { + if (wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "advertised support for " + "AES-128-CMAC, but did not " + "use it"); + return; + } + + if (!wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "did not use HMAC-SHA1-AES " + "with CCMP"); + return; + } + } + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->req_replay_counter_used && + os_memcmp(key->replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + !wpa_replay_counter_valid(sm, key->replay_counter)) { + int i; + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key %s with unexpected " + "replay counter", msgtxt); + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + return; + } + + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + if (sm->wpa_ie == NULL || + sm->wpa_ie_len != key_data_length || + os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + (u8 *) (key + 1), key_data_length); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + break; + case GROUP_2: + if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING + || !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ + case REQUEST: + break; + } + + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + + sm->MICVerified = FALSE; + if (sm->PTK_valid) { + if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sm->req_replay_counter_used = 1; + os_memcpy(sm->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { + /* Supplicant reported a Michael MIC error */ + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + /* Error report is not a request for a new key + * handshake, but since Authenticator may do it, let's + * change the keys now anyway. */ + wpa_request_new_ptk(sm); + } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + /* FIX: why was this triggering PTK rekeying for the + * STA that requested Group Key rekeying?? */ + /* wpa_request_new_ptk(sta->wpa_sm); */ + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); + } + } else { + /* Do not allow the same key replay counter to be reused. This + * does also invalidate all other pending replay counters if + * retransmissions were used, i.e., we will only process one of + * the pending replies and ignore rest if more than one is + * received. */ + sm->key_replay[0].valid = FALSE; + } + +#ifdef CONFIG_PEERKEY + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + + os_free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = os_malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + os_memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->EAPOLKeyReceived = TRUE; + sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); + sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, + u8 *gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN]; + + /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + os_memcpy(data, addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + +#ifdef CONFIG_IEEE80211W + sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#else /* CONFIG_IEEE80211W */ + sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); +} + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); +} + + +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + size_t len; + int alg; + int key_data_len, pad_len = 0; + u8 *buf, *pos; + int version, pairwise; + int i; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (force_version) + version = force_version; + else if (wpa_use_aes_cmac(sm)) + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise == WPA_CIPHER_CCMP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " + "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " + "encr=%d)", + version, + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; + } + + len += key_data_len; + + hdr = os_zalloc(len); + if (hdr == NULL) + return; + hdr->version = wpa_auth->conf.eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); + + key->type = sm->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sm->wpa != WPA_VERSION_WPA2) + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + WPA_PUT_BE16(key->key_info, key_info); + + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; + switch (alg) { + case WPA_CIPHER_CCMP: + WPA_PUT_BE16(key->key_length, 16); + break; + case WPA_CIPHER_TKIP: + WPA_PUT_BE16(key->key_length, 32); + break; + case WPA_CIPHER_WEP40: + WPA_PUT_BE16(key->key_length, 5); + break; + case WPA_CIPHER_WEP104: + WPA_PUT_BE16(key->key_length, 13); + break; + } + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + WPA_PUT_BE16(key->key_length, 0); + + /* FIX: STSL: what to use as key_replay_counter? */ + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay[0].valid = TRUE; + + if (nonce) + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + if (key_rsc) + os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + + if (kde && !encr) { + os_memcpy(key + 1, kde, kde_len); + WPA_PUT_BE16(key->key_data_length, kde_len); + } else if (encr && kde) { + buf = os_zalloc(key_data_len); + if (buf == NULL) { + os_free(hdr); + return; + } + pos = buf; + os_memcpy(pos, kde, kde_len); + pos += kde_len; + + if (pad_len) + *pos++ = 0xdd; + + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + (u8 *) (key + 1))) { + os_free(hdr); + os_free(buf); + return; + } + WPA_PUT_BE16(key->key_data_length, key_data_len); + } else { + u8 ek[32]; + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->PTK.kek, 16); + os_memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + WPA_PUT_BE16(key->key_data_length, key_data_len); + } + os_free(buf); + } + + if (key_info & WPA_KEY_INFO_MIC) { + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); + os_free(hdr); + return; + } + wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, + key->key_mic); + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); + os_free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + int ctr; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); + + ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; + if (ctr == 1) + timeout_ms = eapol_key_timeout_first; + else + timeout_ms = eapol_key_timeout_subseq; + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); +} + + +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info; + int ret = 0; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + os_memcpy(mic, key->key_mic, 16); + os_memset(key->key_mic, 0, 16); + if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key->key_mic) || + os_memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + os_memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +} + + +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +{ + int remove_ptk = 1; + + if (sm == NULL) + return; + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + if (sm->GUpdateStationKeys) { + /* + * Reauthentication cancels the pending group key + * update for this STA. + */ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->PtkGroupInit = TRUE; + } + sm->ReAuthenticationRequest = TRUE; + break; + case WPA_ASSOC_FT: +#ifdef CONFIG_IEEE80211R + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; + return; +#else /* CONFIG_IEEE80211R */ + break; +#endif /* CONFIG_IEEE80211R */ + } + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot && event == WPA_AUTH) + remove_ptk = 0; +#endif /* CONFIG_IEEE80211W */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); + } + + wpa_sm_step(sm); +} + + +static const char * wpa_alg_txt(int alg) +{ + switch (alg) { + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return "WEP"; + default: + return ""; + } +} + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and + * Local AA > Remote AA)) */) { + sm->Pair = TRUE; + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); + sm->TimeoutCtr = 0; + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->wpa_auth, sm->addr); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); + sm->AuthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION2) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + sm->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i does not clear TimeoutCtr here, but this is more + * logical place than INITIALIZE since AUTHENTICATION2 can be + * re-entered on ReAuthenticationRequest without going through + * INITIALIZE. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 msk[2 * PMK_LEN]; + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + os_memcpy(sm->PMK, msk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + if (len >= 2 * PMK_LEN) { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } +#endif /* CONFIG_IEEE80211R */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this + * will break reauthentication since EAPOL state machines may not be + * get into AUTHENTICATING state that clears keyRun before WPA state + * machine enters AUTHENTICATION2 state and goes immediately to INITPMK + * state and takes PMK from the previously used AAA Key. This will + * eventually fail in 4-Way Handshake because Supplicant uses PMK + * derived from the new AAA Key. Setting keyRun = FALSE here seems to + * be good workaround for this issue. */ + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8 *psk; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + if (psk) { + os_memcpy(sm->PMK, psk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +#endif /* CONFIG_IEEE80211R */ + } + sm->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + if (sm->pmksa) + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + } + } + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); +} + + +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + return wpa_auth_derive_ptk_ft(sm, pmk, ptk); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + (u8 *) ptk, sizeof(*ptk), + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + + return 0; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + + /* WPA with IEEE 802.1X: use the derived PMK from EAP + * WPA-PSK: iterate through possible PSKs and select the one matching + * the packet */ + for (;;) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, pmk, &PTK); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); + return; + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); + } + + sm->MICVerified = TRUE; + + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + + if (!sm->mgmt_frame_prot) + return pos; + + igtk.keyid[0] = gsm->GN_igtk; + igtk.keyid[1] = 0; + if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) + < 0) + os_memset(igtk.pn, 0, sizeof(igtk.pn)); + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8 *) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) + */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + char *alg; + int klen; + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = "TKIP"; + klen = 32; + } else { + alg = "CCMP"; + klen = 16; + } + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->wpa_auth->conf.wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf. + wpa_ptk_rekey, 0, wpa_rekey_ptk, + sm->wpa_auth, sm); + } + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); + } + } else { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = TRUE; + else + sm->has_GTK = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + +#ifdef CONFIG_IEEE80211R + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +#endif /* CONFIG_IEEE80211R */ +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (sm->Init) + SM_ENTER(WPA_PTK, INITIALIZE); + else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) + SM_ENTER(WPA_PTK, DISCONNECT); + else if (sm->DeauthenticationRequest) + SM_ENTER(WPA_PTK, DISCONNECTED); + else if (sm->AuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION); + else if (sm->ReAuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION2); + else if (sm->PTKRequest) + SM_ENTER(WPA_PTK, PTKSTART); + else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + case WPA_PTK_AUTHENTICATION: + SM_ENTER(WPA_PTK, AUTHENTICATION2); + break; + case WPA_PTK_AUTHENTICATION2: + if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) + SM_ENTER(WPA_PTK, INITPMK); + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); + break; + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_INITPSK: + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING: + if (sm->MICVerified) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING2: + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK, PTKINITDONE); + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; + + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; + if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gsm->GTK[gsm->GN - 1]; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + os_free(kde); +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init || sm->PtkGroupInit) { + SM_ENTER(WPA_PTK_GROUP, IDLE); + sm->PtkGroupInit = FALSE; + } else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_REKEYNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + !sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + /* FIX: is this the correct way of getting GNonce? */ + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + if (os_get_random(group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to get new random " + "IGTK"); + ret = -1; + } + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + os_memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; +#ifdef CONFIG_IEEE80211W + group->GN_igtk = 4; + group->GM_igtk = 5; +#endif /* CONFIG_IEEE80211W */ + /* GTK[GN] = CalcGTK() */ + wpa_gtk_update(wpa_auth, group); +} + + +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Not in PTKINITDONE; skip Group Key update"); + return 0; + } + if (sm->GUpdateStationKeys) { + /* + * This should not really happen, but just in case, make sure + * we do not count the same STA twice in GKeyDoneStations. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "GUpdateStationKeys already set - do not " + "increment GKeyDoneStations"); + } else { + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + } + wpa_sm_step(sm); + return 0; +} + + +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + /* "GKeyDoneStations = GNoStations" is done in more robust way by + * counting the STAs that are marked with GUpdateStationKeys instead of + * including all STAs that could be in not-yet-completed state. */ + wpa_gtk_update(wpa_auth, group); + + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", + group->GKeyDoneStations); +} + + +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_alg_txt(wpa_auth->conf.wpa_group), + NULL, group->GN, group->GTK[group->GN - 1], + group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", + NULL, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } +} + + +static void wpa_sm_step(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); + return; + } + + sm->in_step_loop = 1; + do { + if (sm->pending_deinit) + break; + + sm->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (sm->pending_deinit) + break; + SM_STEP_RUN(WPA_PTK_GROUP); + if (sm->pending_deinit) + break; + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + } +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + wpa_sm_step(sm); +} + + +void wpa_auth_sm_notify(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} + + +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) +{ + int tmp, i; + struct wpa_group *group; + + if (wpa_auth == NULL) + return; + + group = wpa_auth->group; + + for (i = 0; i < 2; i++) { + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + wpa_gtk_update(wpa_auth, group); + } +} + + +static const char * wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) +{ + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; + + if (wpa_auth == NULL) + return len; + + ret = os_snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" +#ifdef CONFIG_RSN_PREAUTH + "dot11RSNAPreauthenticationImplemented=TRUE\n" +#else /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=FALSE\n" +#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(wpa_auth->conf.wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) +{ + int len = 0, ret; + u32 pairwise = 0; + + if (sm == NULL) + return 0; + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = WPA_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = WPA_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = WPA_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = WPA_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = WPA_CIPHER_SUITE_NONE; + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = RSN_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = RSN_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = RSN_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = RSN_CIPHER_SUITE_NONE; + } else + return 0; + + ret = os_snprintf( + buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_get_pairwise(struct wpa_state_machine *sm) +{ + return sm->pairwise; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + return -1; + + if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol, + WPA_KEY_MGMT_IEEE8021X)) + return 0; + + return -1; +} + + +static struct wpa_group * +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + sm->group = group; + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/hostapd/wpa.h b/hostapd/wpa.h new file mode 100644 index 0000000000000..d3538443ea396 --- /dev/null +++ b/hostapd/wpa.h @@ -0,0 +1,284 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_H +#define WPA_AUTH_H + +#include "eapol_common.h" +#include "wpa_common.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition + */ +struct ft_rrb_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ + le16 action_length; /* little endian length of action_frame */ + u8 ap_address[ETH_ALEN]; + /* + * Followed by action_length bytes of FT Action frame (from Category + * field to the end of Action Frame body. + */ +} STRUCT_PACKED; + +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 + +#define FT_PACKET_REQUEST 0 +#define FT_PACKET_RESPONSE 1 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +#define FT_PACKET_R0KH_R1KH_PULL 200 +#define FT_PACKET_R0KH_R1KH_RESP 201 +#define FT_PACKET_R0KH_R1KH_PUSH 202 + +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + +#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 80 + +struct ft_r0kh_r1kh_pull_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ + le16 data_length; /* little endian length of data (44) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_resp_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ + le16 data_length; /* little endian length of data (76) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; /* copied from pull */ + u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ + u8 s1kh_id[ETH_ALEN]; /* copied from pull */ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_push_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ + le16 data_length; /* little endian length of data (80) */ + u8 ap_address[ETH_ALEN]; + + /* Encrypted with AES key-wrap */ + u8 timestamp[4]; /* current time in seconds since unix epoch, little + * endian */ + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* per STA state machine data */ + +struct wpa_authenticator; +struct wpa_state_machine; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; + + +struct ft_remote_r0kh { + struct ft_remote_r0kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; + u8 key[16]; +}; + + +struct ft_remote_r1kh { + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; + u8 key[16]; +}; + + +struct wpa_auth_config { + int wpa; + int wpa_key_mgmt; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; + int peerkey; + int wme_enabled; + int okc; +#ifdef CONFIG_IEEE80211W + enum { + WPA_NO_IEEE80211W = 0, + WPA_IEEE80211W_OPTIONAL = 1, + WPA_IEEE80211W_REQUIRED = 2 + } ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R +#define SSID_LEN 32 + u8 ssid[SSID_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; + size_t r0_key_holder_len; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ +}; + +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; + +typedef enum { + WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, + WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, + WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx +} wpa_eapol_variable; + +struct wpa_auth_callbacks { + void *ctx; + void (*logger)(void *ctx, const u8 *addr, logger_level level, + const char *txt); + void (*disconnect)(void *ctx, const u8 *addr, u16 reason); + void (*mic_failure_report)(void *ctx, const u8 *addr); + void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, + int value); + int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); + int (*set_key)(void *ctx, int vlan_id, const char *alg, const u8 *addr, + int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*get_seqnum_igtk)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt); + int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), void *cb_ctx); + int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, + void *ctx), void *cb_ctx); + int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, + const u8 *data, size_t data_len); +#endif /* CONFIG_IEEE80211R */ +}; + +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb); +void wpa_deinit(struct wpa_authenticator *wpa_auth); +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, + WPA_INVALID_MDIE, WPA_INVALID_PROTO +}; + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len); +int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm); +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); +void wpa_auth_sta_deinit(struct wpa_state_machine *sm); +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL, WPA_ASSOC_FT +} wpa_event; +void wpa_remove_ptk(struct wpa_state_machine *sm); +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +void wpa_auth_sm_notify(struct wpa_state_machine *sm); +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); +int wpa_auth_pairwise_set(struct wpa_state_machine *sm); +int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, + size_t *len); +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol); +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); + +#ifdef CONFIG_IEEE80211R +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg); +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len), + void *ctx); +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len); +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_H */ diff --git a/hostapd/wpa_auth_i.h b/hostapd/wpa_auth_i.h new file mode 100644 index 0000000000000..bcaeda5dcb08c --- /dev/null +++ b/hostapd/wpa_auth_i.h @@ -0,0 +1,221 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_I_H +#define WPA_AUTH_I_H + +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 4 + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + struct { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } key_replay[RSNA_MAX_EAPOL_RETRIES]; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + Boolean PtkGroupInit; /* init request for PTK Group state machine */ + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int mgmt_frame_prot:1; +#ifdef CONFIG_IEEE80211R + unsigned int ft_completed:1; + unsigned int pmk_r1_name_valid:1; +#endif /* CONFIG_IEEE80211R */ + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth + * Request */ + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ + size_t r0kh_id_len; +#endif /* CONFIG_IEEE80211R */ +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; +#ifdef CONFIG_IEEE80211W + u8 IGTK[2][WPA_IGTK_LEN]; + int GN_igtk, GM_igtk; +#endif /* CONFIG_IEEE80211W */ +}; + + +struct wpa_ft_pmk_cache; + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u32 dot11RSNAAuthenticationSuiteSelected; + u32 dot11RSNAPairwiseCipherSelected; + u32 dot11RSNAGroupCipherSelected; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ + u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ + u32 dot11RSNAGroupCipherRequested; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; + struct wpa_ft_pmk_cache *ft_pmk_cache; +}; + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid); +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt); +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...); +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx); +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +#ifdef CONFIG_PEERKEY +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk); +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_I_H */ diff --git a/hostapd/wpa_auth_ie.c b/hostapd/wpa_auth_ie.c new file mode 100644 index 0000000000000..3ac9d67344b46 --- /dev/null +++ b/hostapd/wpa_auth_ie.c @@ -0,0 +1,864 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "ieee802_11.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "pmksa_cache.h" +#include "wpa_auth_ie.h" +#include "wpa_auth_i.h" + + +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + struct wpa_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct wpa_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += WPA_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + u16 capab; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->rsn_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211W */ + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* RSN Capabilities */ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wme_enabled) { + /* 4 PTKSA replay counters when using WME */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == IEEE80211W_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + if (pos + 2 + PMKID_LEN > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (pos + 2 + 4 > buf + len) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) +{ + u8 *pos, buf[128]; + int res; + + pos = buf; + + if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos, NULL); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_IEEE80211R + if (wpa_auth->conf.wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = os_malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; + + return 0; +} + + +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + os_memcpy(pos, data2, data2_len); + pos += data2_len; + } + return pos; +} + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) + return -1; + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +struct wpa_auth_okc_iter_data { + struct rsn_pmksa_cache_entry *pmksa; + const u8 *aa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_okc_iter_data *data = ctx; + data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa, + data->pmkid); + if (data->pmksa) + return 1; + return 0; +} + + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; + u32 selector; + size_t i; + const u8 *pmkid = NULL; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = WPA_PROTO_RSN; + else + version = WPA_PROTO_WPA; + + if (!(wpa_auth->conf.wpa & version)) { + wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, + version, MAC2STR(sm->addr)); + return WPA_INVALID_PROTO; + } + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); + return WPA_INVALID_AKMP; + } + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + if (version == WPA_PROTO_RSN) + ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; + else + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " + "from " MACSTR, + version == WPA_PROTO_RSN ? "RSN" : "WPA", + data.pairwise_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_PAIRWISE; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { + if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " + "MDIE not included"); + return WPA_INVALID_MDIE; + } + if (os_memcmp(mdie, wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } + } +#endif /* CONFIG_IEEE80211R */ + + if (ciphers & WPA_CIPHER_CCMP) + sm->pairwise = WPA_CIPHER_CCMP; + else + sm->pairwise = WPA_CIPHER_TKIP; + + /* TODO: clear WPA/WPA2 state if STA changes from one to another */ + if (wpa_ie[0] == WLAN_EID_RSN) + sm->wpa = WPA_VERSION_WPA2; + else + sm->wpa = WPA_VERSION_WPA; + + sm->pmksa = NULL; + for (i = 0; i < data.num_pmkid; i++) { + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + pmkid = sm->pmksa->pmkid; + break; + } + } + for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc && + i < data.num_pmkid; i++) { + struct wpa_auth_okc_iter_data idata; + idata.pmksa = NULL; + idata.aa = wpa_auth->addr; + idata.spa = sm->addr; + idata.pmkid = &data.pmkid[i * PMKID_LEN]; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata); + if (idata.pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "OKC match for PMKID"); + sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa, + idata.pmksa, + wpa_auth->addr, + idata.pmkid); + pmkid = idata.pmkid; + break; + } + } + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); + } + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + + return WPA_IE_OK; +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +int wpa_auth_uses_mfp(struct wpa_state_machine *sm) +{ + return sm ? sm->mgmt_frame_prot : 0; +} diff --git a/hostapd/wpa_auth_ie.h b/hostapd/wpa_auth_ie.h new file mode 100644 index 0000000000000..9968d2d92a162 --- /dev/null +++ b/hostapd/wpa_auth_ie.h @@ -0,0 +1,54 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_IE_H +#define WPA_AUTH_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len); +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); + +#endif /* WPA_AUTH_IE_H */ diff --git a/hostapd/wpa_ft.c b/hostapd/wpa_ft.c new file mode 100644 index 0000000000000..9cf6713ffd659 --- /dev/null +++ b/hostapd/wpa_ft.c @@ -0,0 +1,1499 @@ +/* + * hostapd - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "wpa.h" +#include "aes_wrap.h" +#include "ieee802_11.h" +#include "defs.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + + +#ifdef CONFIG_IEEE80211R + +static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, + const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ether == NULL) + return -1; + return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ft_action == NULL) + return -1; + return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, + data, data_len); +} + + +static struct wpa_state_machine * +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (wpa_auth->cb.add_sta == NULL) + return NULL; + return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); +} + + +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + u8 *pos = buf; + u8 capab; + if (len < 2 + sizeof(struct rsn_mdie)) + return -1; + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; + os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + capab = RSN_FT_CAPAB_FT_OVER_DS; + *pos++ = capab; + + return pos - buf; +} + + +static int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len) +{ + u8 *pos = buf, *ielen; + struct rsn_ftie *hdr; + + if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + subelem_len) + return -1; + + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ielen = pos++; + + hdr = (struct rsn_ftie *) pos; + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + + /* Optional Parameters */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + + if (r0kh_id) { + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + } + + if (subelem) { + os_memcpy(pos, subelem, subelem_len); + pos += subelem_len; + } + + *ielen = pos - buf - 2; + + return pos - buf; +} + + +struct wpa_ft_pmk_r0_sa { + struct wpa_ft_pmk_r0_sa *next; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + int pmk_r1_pushed; +}; + +struct wpa_ft_pmk_r1_sa { + struct wpa_ft_pmk_r1_sa *next; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ +}; + +struct wpa_ft_pmk_cache { + struct wpa_ft_pmk_r0_sa *pmk_r0; + struct wpa_ft_pmk_r1_sa *pmk_r1; +}; + +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) +{ + struct wpa_ft_pmk_cache *cache; + + cache = os_zalloc(sizeof(*cache)); + + return cache; +} + + +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) +{ + struct wpa_ft_pmk_r0_sa *r0, *r0prev; + struct wpa_ft_pmk_r1_sa *r1, *r1prev; + + r0 = cache->pmk_r0; + while (r0) { + r0prev = r0; + r0 = r0->next; + os_memset(r0prev->pmk_r0, 0, PMK_LEN); + os_free(r0prev); + } + + r1 = cache->pmk_r1; + while (r1) { + r1prev = r1; + r1 = r1->next; + os_memset(r1prev->pmk_r1, 0, PMK_LEN); + os_free(r1prev); + } + + os_free(cache); +} + + +static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0, + const u8 *pmk_r0_name) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + /* TODO: add expiration and limit on number of entries in cache */ + + r0 = os_zalloc(sizeof(*r0)); + if (r0 == NULL) + return -1; + + os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(r0->spa, spa, ETH_ALEN); + + r0->next = cache->pmk_r0; + cache->pmk_r0 = r0; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0_name, + u8 *pmk_r0) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + r0 = cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); + return 0; + } + + r0 = r0->next; + } + + return -1; +} + + +static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1, + const u8 *pmk_r1_name) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + /* TODO: add expiration and limit on number of entries in cache */ + + r1 = os_zalloc(sizeof(*r1)); + if (r1 == NULL) + return -1; + + os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(r1->spa, spa, ETH_ALEN); + + r1->next = cache->pmk_r1; + cache->pmk_r1 = r1; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + r1 = cache->pmk_r1; + while (r1) { + if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + return 0; + } + + r1 = r1->next; + } + + return -1; +} + + +static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *s1kh_id, const u8 *r0kh_id, + size_t r0kh_id_len, const u8 *pmk_r0_name) +{ + struct ft_remote_r0kh *r0kh; + struct ft_r0kh_r1kh_pull_frame frame, f; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh->id_len == r0kh_id_len && + os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " + "address " MACSTR, MAC2STR(r0kh->addr)); + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + if (os_get_random(f.nonce, sizeof(f.nonce))) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } + os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + + if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + f.nonce, frame.nonce) < 0) + return -1; + + wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + + return 0; +} + + +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *mdid = sm->wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; + size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; + const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; + const u8 *ssid = sm->wpa_auth->conf.ssid; + size_t ssid_len = sm->wpa_auth->conf.ssid_len; + + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name); + + wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, + pmk_r1, pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_name); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + (u8 *) ptk, sizeof(*ptk), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +#ifdef CONFIG_IEEE80211W +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} +#endif /* CONFIG_IEEE80211W */ + + +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem; + struct wpa_group *gsm = sm->group; + size_t subelem_len, pad_len; + const u8 *key; + size_t key_len; + u8 keybuf[32]; + + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return NULL; + + /* + * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less + * than 16 bytes. + */ + pad_len = key_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + if (key_len + pad_len < 16) + pad_len += 8; + if (pad_len) { + os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); + os_memset(keybuf + key_len, 0, pad_len); + keybuf[key_len] = 0xdd; + key_len += pad_len; + key = keybuf; + } else + key = gsm->GTK[gsm->GN - 1]; + + /* + * Sub-elem ID[1] | Length[1] | Key Info[1] | Key Length[1] | RSC[8] | + * Key[5..32]. + */ + subelem_len = 12 + key_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + subelem[0] = FTIE_SUBELEM_GTK; + subelem[1] = 10 + key_len + 8; + subelem[2] = gsm->GN & 0x03; /* Key ID in B0-B1 of Key Info */ + subelem[3] = gsm->GTK_len; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 4); + if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 12)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + + /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | + * Key[16+8] */ + subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + pos = subelem; + *pos++ = FTIE_SUBELEM_IGTK; + *pos++ = subelem_len - 2; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + pos += 6; + *pos++ = WPA_IGTK_LEN; + if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + gsm->IGTK[gsm->GN_igtk - 4], pos)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} +#endif /* CONFIG_IEEE80211W */ + + +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg) +{ + u8 *end, *mdie, *ftie, *rsnie, *r0kh_id, *subelem = NULL; + size_t mdie_len, ftie_len, rsnie_len, r0kh_id_len, subelem_len = 0; + int res; + struct wpa_auth_config *conf; + struct rsn_ftie *_ftie; + + if (sm == NULL) + return pos; + + conf = &sm->wpa_auth->conf; + + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + return pos; + + end = pos + max_len; + + /* RSN */ + res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); + if (res < 0) + return pos; + rsnie = pos; + rsnie_len = res; + pos += res; + + /* Mobility Domain Information */ + res = wpa_write_mdie(conf, pos, end - pos); + if (res < 0) + return pos; + mdie = pos; + mdie_len = res; + pos += res; + + /* Fast BSS Transition Information */ + if (auth_alg == WLAN_AUTH_FT) { + subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + r0kh_id = sm->r0kh_id; + r0kh_id_len = sm->r0kh_id_len; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot) { + u8 *igtk; + size_t igtk_len; + u8 *nbuf; + igtk = wpa_ft_igtk_subelem(sm, &igtk_len); + if (igtk == NULL) { + os_free(subelem); + return pos; + } + nbuf = os_realloc(subelem, subelem_len + igtk_len); + if (nbuf == NULL) { + os_free(subelem); + os_free(igtk); + return pos; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, igtk, igtk_len); + subelem_len += igtk_len; + os_free(igtk); + } +#endif /* CONFIG_IEEE80211W */ + } else { + r0kh_id = conf->r0_key_holder; + r0kh_id_len = conf->r0_key_holder_len; + } + res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, NULL, NULL, pos, + end - pos, subelem, subelem_len); + os_free(subelem); + if (res < 0) + return pos; + ftie = pos; + ftie_len = res; + pos += res; + + _ftie = (struct rsn_ftie *) (ftie + 2); + _ftie->mic_control[1] = 3; /* Information element count */ + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, + mdie, mdie_len, ftie, ftie_len, + rsnie, rsnie_len, NULL, 0, _ftie->mic) < 0) + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + + return pos; +} + + +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; +}; + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static void wpa_ft_install_ptk(struct wpa_state_machine *sm) +{ + char *alg; + int klen; + + /* MLME-SETKEYS.request(PTK) */ + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = "TKIP"; + klen = 32; + } else if (sm->pairwise == WPA_CIPHER_CCMP) { + alg = "CCMP"; + klen = 16; + } else + return; + + /* FIX: add STA entry to kernel/driver here? The set_key will fail + * most likely without this.. At the moment, STA entry is added only + * after association has been completed. Alternatively, could + * re-configure PTK at that point(?). + */ + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) + return; + + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; +} + + +static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + u8 **resp_ies, size_t *resp_ies_len) +{ + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wpa_auth_config *conf; + struct wpa_ft_ies parse; + size_t buflen; + int ret; + u8 *pos, *end; + + *resp_ies = NULL; + *resp_ies_len = 0; + + sm->pmk_r1_name_valid = 0; + conf = &sm->wpa_auth->conf; + + wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", + ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); + return WLAN_STATUS_INVALID_FTIE; + } + + wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", + parse.r0kh_id, parse.r0kh_id_len); + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", + parse.rsn_pmkid, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name); + wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", + pmk_r1_name, WPA_PMK_NAME_LEN); + + if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1) < + 0) { + if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, + sm->r0kh_id_len, parse.rsn_pmkid) < 0) { + wpa_printf(MSG_DEBUG, "FT: Did not have matching " + "PMK-R1 and unknown R0KH-ID"); + return WLAN_STATUS_INVALID_PMKID; + } + + /* + * TODO: Should return "status pending" (and the caller should + * not send out response now). The real response will be sent + * once the response from R0KH is received. + */ + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); + sm->pmk_r1_name_valid = 1; + os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + + if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "ANonce"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + sm->SNonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", + sm->ANonce, WPA_NONCE_LEN); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + (u8 *) &sm->PTK, sizeof(sm->PTK), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->PTK, sizeof(sm->PTK)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + wpa_ft_install_ptk(sm); + + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + FT_R1KH_ID_LEN + 200; + *resp_ies = os_zalloc(buflen); + if (*resp_ies == NULL) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + pos = *resp_ies; + end = *resp_ies + buflen; + + ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_mdie(conf, pos, end - pos); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + *resp_ies_len = pos - *resp_ies; + + return WLAN_STATUS_SUCCESS; +} + + +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len), + void *ctx) +{ + u16 status; + u8 *resp_ies; + size_t resp_ies_len; + + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " + "WPA SM not available"); + return; + } + + wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR + " BSSID=" MACSTR " transaction=%d", + MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); + status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR + " auth_transaction=%d status=%d", + MAC2STR(sm->addr), auth_transaction + 1, status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + cb(ctx, sm->addr, bssid, auth_transaction + 1, status, + resp_ies, resp_ies_len); + os_free(resp_ies); +} + + +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 mic[16]; + + if (sm == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn == NULL) { + wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " + "with the PMKR1Name derived from auth request"); + return WLAN_STATUS_INVALID_PMKID; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + /* + * Assume that MDIE, FTIE, and RSN IE are protected and that there is + * no RIC, so total of 3 protected IEs. + */ + if (ftie->mic_control[1] != 3) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", + ftie->mic_control[1]); + return WLAN_STATUS_INVALID_FTIE; + } + + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, NULL, 0, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return WLAN_STATUS_INVALID_FTIE; + } + + return WLAN_STATUS_SUCCESS; +} + + +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) +{ + const u8 *sta_addr, *target_ap; + const u8 *ies; + size_t ies_len; + u8 action; + struct ft_rrb_frame *frame; + + if (sm == NULL) + return -1; + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * FT Request action frame body[variable] + */ + + if (len < 14) { + wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " + "(len=%lu)", (unsigned long) len); + return -1; + } + + action = data[1]; + sta_addr = data + 2; + target_ap = data + 8; + ies = data + 14; + ies_len = len - 14; + + wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR + " Target AP=" MACSTR " Action=%d)", + MAC2STR(sta_addr), MAC2STR(target_ap), action); + + if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " + "STA=" MACSTR " STA-Address=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sta_addr)); + return -1; + } + + /* + * Do some sanity checking on the target AP address (not own and not + * broadcast. This could be extended to filter based on a list of known + * APs in the MD (if such a list were configured). + */ + if ((target_ap[0] & 0x01) || + os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " + "frame"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + + /* RRB - Forward action frame to the target AP */ + frame = os_malloc(sizeof(*frame) + len); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_REQUEST; + frame->action_length = host_to_le16(len); + os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); + os_memcpy(frame + 1, data, len); + + wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, + sizeof(*frame) + len); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, + const u8 *current_ap, const u8 *sta_addr, + const u8 *body, size_t len) +{ + struct wpa_state_machine *sm; + u16 status; + u8 *resp_ies, *pos; + size_t resp_ies_len, rlen; + struct ft_rrb_frame *frame; + + sm = wpa_ft_add_sta(wpa_auth, sta_addr); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " + "RRB Request"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); + + status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR + " CurrentAP=" MACSTR " status=%d", + MAC2STR(sm->addr), MAC2STR(current_ap), status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + + /* RRB - Forward action frame response to the Current AP */ + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * Status_Code[2] FT Request action frame body[variable] + */ + rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; + + frame = os_malloc(sizeof(*frame) + rlen); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_RESPONSE; + frame->action_length = host_to_le16(rlen); + os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); + pos = (u8 *) (frame + 1); + *pos++ = WLAN_ACTION_FT; + *pos++ = 2; /* Action: Response */ + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, wpa_auth->addr, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE16(pos, status); + pos += 2; + if (resp_ies) { + os_memcpy(pos, resp_ies, resp_ies_len); + os_free(resp_ies); + } + + wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, + sizeof(*frame) + rlen); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_remote_r1kh *r1kh; + struct ft_r0kh_r1kh_resp_frame resp, r; + u8 pmk_r0[PMK_LEN]; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + + if (data_len < sizeof(*frame)) + return -1; + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) + break; + r1kh = r1kh->next; + } + if (r1kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " + "PMK-R1 pull source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_pull_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", + f.pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; + resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); + os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); + os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); + if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0) < + 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " + "PMK-R1 pull"); + return -1; + } + + wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, + r.pmk_r1, r.pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, + WPA_PMK_NAME_LEN); + + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + r.nonce, resp.nonce) < 0) { + os_memset(pmk_r0, 0, PMK_LEN); + return -1; + } + + os_memset(pmk_r0, 0, PMK_LEN); + + wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + + return 0; +} + + +static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_remote_r0kh *r0kh; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 pull response source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_resp_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "response from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " + "matching R1KH-ID"); + return -1; + } + + /* TODO: verify that <nonce,s1kh_id> matches with a pending request + * and call this requests callback function to finish request + * processing */ + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_remote_r0kh *r0kh; + struct os_time now; + os_time_t tsend; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 push source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_push_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + frame->timestamp, f.timestamp) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " + MACSTR, MAC2STR(src_addr)); + return -1; + } + + os_get_time(&now); + tsend = WPA_GET_LE32(f.timestamp); + if ((now.sec > tsend && now.sec - tsend > 60) || + (now.sec < tsend && tsend - now.sec > 60)) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " + "timestamp: sender time %d own time %d\n", + (int) tsend, (int) now.sec); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " + "R1KH-ID (received " MACSTR " own " MACSTR ")", + MAC2STR(f.r1kh_id), + MAC2STR(wpa_auth->conf.r1_key_holder)); + return -1; + } + + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_rrb_frame *frame; + u16 alen; + const u8 *pos, *end, *start; + u8 action; + const u8 *sta_addr, *target_ap_addr; + + wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, + MAC2STR(src_addr)); + + if (data_len < sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", + (unsigned long) data_len); + return -1; + } + + pos = data; + frame = (struct ft_rrb_frame *) pos; + pos += sizeof(*frame); + + alen = le_to_host16(frame->action_length); + wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " + "action_length=%d ap_address=" MACSTR, + frame->frame_type, frame->packet_type, alen, + MAC2STR(frame->ap_address)); + + if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { + /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " + "unrecognized type %d", frame->frame_type); + return -1; + } + + if (alen > data_len - sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " + "frame"); + return -1; + } + + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) + return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) + return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) + return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); + + if (alen < 1 + 1 + 2 * ETH_ALEN) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " + "room for Action Frame body); alen=%lu", + (unsigned long) alen); + return -1; + } + start = pos; + end = pos + alen; + + if (*pos != WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " + "%d", *pos); + return -1; + } + + pos++; + action = *pos++; + sta_addr = pos; + pos += ETH_ALEN; + target_ap_addr = pos; + pos += ETH_ALEN; + wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" + MACSTR " target_ap_addr=" MACSTR, + action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); + + if (frame->packet_type == FT_PACKET_REQUEST) { + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); + + if (action != 1) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " + "RRB Request", action); + return -1; + } + + if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Target AP address in the " + "RRB Request does not match with own " + "address"); + return -1; + } + + if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, + sta_addr, pos, end - pos) < 0) + return -1; + } else if (frame->packet_type == FT_PACKET_RESPONSE) { + u16 status_code; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "FT: Not enough room for status " + "code in RRB Response"); + return -1; + } + status_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " + "(status_code=%d)", status_code); + + if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " + "packet_type %d", frame->packet_type); + return -1; + } + + return 0; +} + + +static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id) +{ + struct ft_r0kh_r1kh_push_frame frame, f; + struct os_time now; + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, + s1kh_id, f.pmk_r1, f.pmk_r1_name); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, + WPA_PMK_NAME_LEN); + os_get_time(&now); + WPA_PUT_LE32(f.timestamp, now.sec); + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + f.timestamp, frame.timestamp) < 0) + return; + + wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); +} + + +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_ft_pmk_r0_sa *r0; + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.pmk_r1_push) + return; + + r0 = wpa_auth->ft_pmk_cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + break; + r0 = r0->next; + } + + if (r0 == NULL || r0->pmk_r1_pushed) + return; + r0->pmk_r1_pushed = 1; + + wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " + "for STA " MACSTR, MAC2STR(addr)); + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); + r1kh = r1kh->next; + } +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/hostapd/wps_hostapd.c b/hostapd/wps_hostapd.c new file mode 100644 index 0000000000000..a824c16dfbce4 --- /dev/null +++ b/hostapd/wps_hostapd.c @@ -0,0 +1,998 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "driver.h" +#include "eloop.h" +#include "uuid.h" +#include "wpa_ctrl.h" +#include "ieee802_11_defs.h" +#include "sta_info.h" +#include "eapol_sm.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" +#include "wps/wps_dev_attr.h" +#include "wps_hostapd.h" + + +#ifdef CONFIG_WPS_UPNP +#include "wps/wps_upnp.h" +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps); +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); +#endif /* CONFIG_WPS_UPNP */ + + +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len) +{ + struct hostapd_data *hapd = ctx; + struct hostapd_wpa_psk *p; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " + MACSTR, MAC2STR(mac_addr)); + wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); + + if (psk_len != PMK_LEN) { + wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", + (unsigned long) psk_len); + return -1; + } + + /* Add the new PSK to runtime PSK list */ + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + os_memcpy(p->psk, psk, PMK_LEN); + + p->next = ssid->wpa_psk; + ssid->wpa_psk = p; + + if (ssid->wpa_psk_file) { + FILE *f; + char hex[PMK_LEN * 2 + 1]; + /* Add the new PSK to PSK list file */ + f = fopen(ssid->wpa_psk_file, "a"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add the PSK to " + "'%s'", ssid->wpa_psk_file); + return -1; + } + + wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); + fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); + fclose(f); + } + + return 0; +} + + +static int hostapd_wps_set_ie_cb(void *ctx, const u8 *beacon_ie, + size_t beacon_ie_len, const u8 *probe_resp_ie, + size_t probe_resp_ie_len) +{ + struct hostapd_data *hapd = ctx; + + os_free(hapd->wps_beacon_ie); + if (beacon_ie_len == 0) { + hapd->wps_beacon_ie = NULL; + hapd->wps_beacon_ie_len = 0; + } else { + hapd->wps_beacon_ie = os_malloc(beacon_ie_len); + if (hapd->wps_beacon_ie == NULL) { + hapd->wps_beacon_ie_len = 0; + return -1; + } + os_memcpy(hapd->wps_beacon_ie, beacon_ie, beacon_ie_len); + hapd->wps_beacon_ie_len = beacon_ie_len; + } + hostapd_set_wps_beacon_ie(hapd, hapd->wps_beacon_ie, + hapd->wps_beacon_ie_len); + + os_free(hapd->wps_probe_resp_ie); + if (probe_resp_ie_len == 0) { + hapd->wps_probe_resp_ie = NULL; + hapd->wps_probe_resp_ie_len = 0; + } else { + hapd->wps_probe_resp_ie = os_malloc(probe_resp_ie_len); + if (hapd->wps_probe_resp_ie == NULL) { + hapd->wps_probe_resp_ie_len = 0; + return -1; + } + os_memcpy(hapd->wps_probe_resp_ie, probe_resp_ie, + probe_resp_ie_len); + hapd->wps_probe_resp_ie_len = probe_resp_ie_len; + } + hostapd_set_wps_probe_resp_ie(hapd, hapd->wps_probe_resp_ie, + hapd->wps_probe_resp_ie_len); + + return 0; +} + + +static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + struct hostapd_data *hapd = ctx; + char uuid[40], txt[400]; + int len; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); + len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED + "%s " MACSTR " [%s|%s|%s|%s|%s|%d-%08X-%d]", + uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, + dev->model_number, dev->serial_number, + dev->categ, dev->oui, dev->sub_categ); + if (len > 0 && len < (int) sizeof(txt)) + wpa_msg(hapd, MSG_INFO, "%s", txt); + + if (hapd->conf->wps_pin_requests) { + FILE *f; + struct os_time t; + f = fopen(hapd->conf->wps_pin_requests, "a"); + if (f == NULL) + return; + os_get_time(&t); + fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" + "\t%d-%08X-%d\n", + t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, dev->model_number, + dev->serial_number, + dev->categ, dev->oui, dev->sub_categ); + fclose(f); + } +} + + +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, + const u8 *uuid_e) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_msg(hapd, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", + MAC2STR(mac_addr), uuid); +} + + +static int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} + + +static void wps_reload_config(void *eloop_data, void *user_ctx) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); + if (hostapd_reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " + "configuration"); + } +} + + +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + FILE *oconf, *nconf; + size_t len, i; + char *tmp_fname; + char buf[1024]; + int multi_bss; + int wpa; + + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", + cred->cred_attr, cred->cred_attr_len); + + wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", + cred->auth_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, + MAC2STR(cred->mac_addr)); + + if ((hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { + size_t blen = cred->cred_attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, + cred->cred_attr, cred->cred_attr_len); + wpa_msg(hapd, MSG_INFO, "%s%s", + WPS_EVENT_NEW_AP_SETTINGS, buf); + os_free(buf); + } + } else + wpa_msg(hapd, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); + + if (hapd->conf->wps_cred_processing == 1) + return 0; + + len = os_strlen(hapd->iface->config_fname) + 5; + tmp_fname = os_malloc(len); + if (tmp_fname == NULL) + return -1; + os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); + + oconf = fopen(hapd->iface->config_fname, "r"); + if (oconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not open current " + "configuration file"); + os_free(tmp_fname); + return -1; + } + + nconf = fopen(tmp_fname, "w"); + if (nconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not write updated " + "configuration file"); + os_free(tmp_fname); + fclose(oconf); + return -1; + } + + fprintf(nconf, "# WPS configuration - START\n"); + + fprintf(nconf, "wps_state=2\n"); + + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + wpa = 1; + else + wpa = 0; + + if (wpa) { + char *prefix; + fprintf(nconf, "wpa=%d\n", wpa); + + fprintf(nconf, "wpa_key_mgmt="); + prefix = ""; + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { + fprintf(nconf, "WPA-EAP"); + prefix = " "; + } + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + fprintf(nconf, "%sWPA-PSK", prefix); + fprintf(nconf, "\n"); + + fprintf(nconf, "wpa_pairwise="); + prefix = ""; + if (cred->encr_type & WPS_ENCR_AES) { + fprintf(nconf, "CCMP"); + prefix = " "; + } + if (cred->encr_type & WPS_ENCR_TKIP) { + fprintf(nconf, "%sTKIP", prefix); + } + fprintf(nconf, "\n"); + + if (cred->key_len >= 8 && cred->key_len < 64) { + fprintf(nconf, "wpa_passphrase="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else if (cred->key_len == 64) { + fprintf(nconf, "wpa_psk="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else { + wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " + "for WPA/WPA2", + (unsigned long) cred->key_len); + } + + fprintf(nconf, "auth_algs=1\n"); + } else { + if ((cred->auth_type & WPS_AUTH_OPEN) && + (cred->auth_type & WPS_AUTH_SHARED)) + fprintf(nconf, "auth_algs=3\n"); + else if (cred->auth_type & WPS_AUTH_SHARED) + fprintf(nconf, "auth_algs=2\n"); + else + fprintf(nconf, "auth_algs=1\n"); + + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx < 4) { + fprintf(nconf, "wep_default_key=%d\n", cred->key_idx); + fprintf(nconf, "wep_key%d=", cred->key_idx); + if (cred->key_len != 10 && cred->key_len != 26) + fputc('"', nconf); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + if (cred->key_len != 10 && cred->key_len != 26) + fputc('"', nconf); + fprintf(nconf, "\n"); + } + } + + fprintf(nconf, "# WPS configuration - END\n"); + + multi_bss = 0; + while (fgets(buf, sizeof(buf), oconf)) { + if (os_strncmp(buf, "bss=", 4) == 0) + multi_bss = 1; + if (!multi_bss && + (str_starts(buf, "ssid=") || + str_starts(buf, "auth_algs=") || + str_starts(buf, "wps_state=") || + str_starts(buf, "wpa=") || + str_starts(buf, "wpa_psk=") || + str_starts(buf, "wpa_pairwise=") || + str_starts(buf, "rsn_pairwise=") || + str_starts(buf, "wpa_key_mgmt=") || + str_starts(buf, "wpa_passphrase="))) { + fprintf(nconf, "#WPS# %s", buf); + } else + fprintf(nconf, "%s", buf); + } + + fclose(nconf); + fclose(oconf); + + if (rename(tmp_fname, hapd->iface->config_fname) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " + "configuration file: %s", strerror(errno)); + os_free(tmp_fname); + return -1; + } + + os_free(tmp_fname); + + /* Schedule configuration reload after short period of time to allow + * EAP-WSC to be finished. + */ + eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, + NULL); + + /* TODO: dualband AP may need to update multiple configuration files */ + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); + + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + FILE *f; + + if (!data->enrollee) + return; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup + * if this happens multiple times. + */ + hapd->ap_pin_failures++; + if (hapd->ap_pin_failures < 4) + return; + + wpa_msg(hapd, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); + hapd->wps->ap_setup_locked = 1; + + wps_registrar_update_ie(hapd->wps->registrar); + + if (hapd->conf->wps_cred_processing == 1) + return; + + f = fopen(hapd->iface->config_fname, "a"); + if (f == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not append to the current " + "configuration file"); + return; + } + + fprintf(f, "# WPS AP Setup Locked based on possible attack\n"); + fprintf(f, "ap_setup_locked=1\n"); + fclose(f); + + /* TODO: dualband AP may need to update multiple configuration files */ + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); +} + + +static void hostapd_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + if (event == WPS_EV_PWD_AUTH_FAIL) + hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); +} + + +static void hostapd_wps_clear_ies(struct hostapd_data *hapd) +{ + os_free(hapd->wps_beacon_ie); + hapd->wps_beacon_ie = NULL; + hapd->wps_beacon_ie_len = 0; + hostapd_set_wps_beacon_ie(hapd, NULL, 0); + + os_free(hapd->wps_probe_resp_ie); + hapd->wps_probe_resp_ie = NULL; + hapd->wps_probe_resp_ie_len = 0; + hostapd_set_wps_probe_resp_ie(hapd, NULL, 0); +} + + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct wps_context *wps; + struct wps_registrar_config cfg; + + if (conf->wps_state == 0) { + hostapd_wps_clear_ies(hapd); + return 0; + } + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = hostapd_wps_cred_cb; + wps->event_cb = hostapd_wps_event_cb; + wps->cb_ctx = hapd; + + os_memset(&cfg, 0, sizeof(cfg)); + wps->wps_state = hapd->conf->wps_state; + wps->ap_setup_locked = hapd->conf->ap_setup_locked; + if (is_nil_uuid(hapd->conf->uuid)) { + uuid_gen_mac_addr(hapd->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", + wps->uuid, UUID_LEN); + } else + os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); + wps->ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); + wps->ap = 1; + os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); + wps->dev.device_name = hapd->conf->device_name ? + os_strdup(hapd->conf->device_name) : NULL; + wps->dev.manufacturer = hapd->conf->manufacturer ? + os_strdup(hapd->conf->manufacturer) : NULL; + wps->dev.model_name = hapd->conf->model_name ? + os_strdup(hapd->conf->model_name) : NULL; + wps->dev.model_number = hapd->conf->model_number ? + os_strdup(hapd->conf->model_number) : NULL; + wps->dev.serial_number = hapd->conf->serial_number ? + os_strdup(hapd->conf->serial_number) : NULL; + if (hapd->conf->config_methods) { + char *m = hapd->conf->config_methods; + if (os_strstr(m, "label")) + wps->config_methods |= WPS_CONFIG_LABEL; + if (os_strstr(m, "display")) + wps->config_methods |= WPS_CONFIG_DISPLAY; + if (os_strstr(m, "push_button")) + wps->config_methods |= WPS_CONFIG_PUSHBUTTON; + if (os_strstr(m, "keypad")) + wps->config_methods |= WPS_CONFIG_KEYPAD; + } + if (hapd->conf->device_type) { + char *pos; + u8 oui[4]; + /* <categ>-<OUI>-<subcateg> */ + wps->dev.categ = atoi(hapd->conf->device_type); + pos = os_strchr(hapd->conf->device_type, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + if (hexstr2bin(pos, oui, 4)) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI"); + os_free(wps); + return -1; + } + wps->dev.oui = WPA_GET_BE32(oui); + pos = os_strchr(pos, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + wps->dev.sub_categ = atoi(pos); + } + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); + wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + + if (conf->wpa & WPA_PROTO_RSN) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPA2PSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->wpa & WPA_PROTO_WPA) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPAPSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { + wps->encr_types |= WPS_ENCR_NONE; + wps->auth_types |= WPS_AUTH_OPEN; + } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { + wps->encr_types |= WPS_ENCR_WEP; + if (conf->auth_algs & WPA_AUTH_ALG_OPEN) + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->auth_algs & WPA_AUTH_ALG_SHARED) + wps->auth_types |= WPS_AUTH_SHARED; + } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->default_wep_key_len) + wps->encr_types |= WPS_ENCR_WEP; + else + wps->encr_types |= WPS_ENCR_NONE; + } + + if (conf->ssid.wpa_psk_file) { + /* Use per-device PSKs */ + } else if (conf->ssid.wpa_passphrase) { + wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); + wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); + } else if (conf->ssid.wpa_psk) { + wps->network_key = os_malloc(2 * PMK_LEN + 1); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, + conf->ssid.wpa_psk->psk, PMK_LEN); + wps->network_key_len = 2 * PMK_LEN; + } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { + wps->network_key = os_malloc(conf->ssid.wep.len[0]); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + os_memcpy(wps->network_key, conf->ssid.wep.key[0], + conf->ssid.wep.len[0]); + wps->network_key_len = conf->ssid.wep.len[0]; + } + + if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { + /* Override parameters to enable security by default */ + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + } + + wps->ap_settings = conf->ap_settings; + wps->ap_settings_len = conf->ap_settings_len; + + cfg.new_psk_cb = hostapd_wps_new_psk_cb; + cfg.set_ie_cb = hostapd_wps_set_ie_cb; + cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; + cfg.reg_success_cb = hostapd_wps_reg_success_cb; + cfg.cb_ctx = hapd; + cfg.skip_cred_build = conf->skip_cred_build; + cfg.extra_cred = conf->extra_cred; + cfg.extra_cred_len = conf->extra_cred_len; + cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && + conf->skip_cred_build; + + wps->registrar = wps_registrar_init(wps, &cfg); + if (wps->registrar == NULL) { + printf("Failed to initialize WPS Registrar\n"); + os_free(wps->network_key); + os_free(wps); + return -1; + } + +#ifdef CONFIG_WPS_UPNP + wps->friendly_name = hapd->conf->friendly_name; + wps->manufacturer_url = hapd->conf->manufacturer_url; + wps->model_description = hapd->conf->model_description; + wps->model_url = hapd->conf->model_url; + wps->upc = hapd->conf->upc; + + if (hostapd_wps_upnp_init(hapd, wps) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); + wps_registrar_deinit(wps->registrar); + os_free(wps->network_key); + os_free(wps); + return -1; + } +#endif /* CONFIG_WPS_UPNP */ + + hapd->wps = wps; + + return 0; +} + + +void hostapd_deinit_wps(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return; +#ifdef CONFIG_WPS_UPNP + hostapd_wps_upnp_deinit(hapd); +#endif /* CONFIG_WPS_UPNP */ + wps_registrar_deinit(hapd->wps->registrar); + os_free(hapd->wps->network_key); + wps_device_data_free(&hapd->wps->dev); + wps_free_pending_msgs(hapd->wps->upnp_msgs); + os_free(hapd->wps); + hapd->wps = NULL; + hostapd_wps_clear_ies(hapd); +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin) +{ + u8 u[UUID_LEN]; + int any = 0; + + if (hapd->wps == NULL) + return -1; + if (os_strcmp(uuid, "any") == 0) + any = 1; + else if (uuid_str2bin(uuid, u)) + return -1; + return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, + (const u8 *) pin, os_strlen(pin)); +} + + +int hostapd_wps_button_pushed(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return -1; + return wps_registrar_button_pushed(hapd->wps->registrar); +} + + +void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct wpabuf *wps_ie; + const u8 *end, *pos, *wps; + + if (hapd->wps == NULL) + return; + + pos = ie; + end = ie + ie_len; + wps = NULL; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + return; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) { + wps = pos; + break; + } + pos += 2 + pos[1]; + } + + if (wps == NULL) + return; /* No WPS IE in Probe Request */ + + wps_ie = wpabuf_alloc(ie_len); + if (wps_ie == NULL) + return; + + /* There may be multiple WPS IEs in the message, so need to concatenate + * their WPS Data fields */ + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) + wpabuf_put_data(wps_ie, pos + 6, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(wps_ie) > 0) { + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); +#ifdef CONFIG_WPS_UPNP + /* FIX: what exactly should be included in the WLANEvent? + * WPS attributes? Full ProbeReq frame? */ + upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, + UPNP_WPS_WLANEVENT_TYPE_PROBE, + wps_ie); +#endif /* CONFIG_WPS_UPNP */ + } + + wpabuf_free(wps_ie); +} + + +#ifdef CONFIG_WPS_UPNP + +static struct wpabuf * +hostapd_rx_req_get_device_info(void *priv, struct upnp_wps_peer *peer) +{ + struct hostapd_data *hapd = priv; + struct wps_config cfg; + struct wps_data *wps; + enum wsc_op_code op_code; + struct wpabuf *m1; + + /* + * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS + * registration over UPnP with the AP acting as an Enrollee. It should + * be noted that this is frequently used just to get the device data, + * i.e., there may not be any intent to actually complete the + * registration. + */ + + if (peer->wps) + wps_deinit(peer->wps); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = hapd->wps; + cfg.pin = (u8 *) hapd->conf->ap_pin; + cfg.pin_len = os_strlen(hapd->conf->ap_pin); + wps = wps_init(&cfg); + if (wps == NULL) + return NULL; + + m1 = wps_get_msg(wps, &op_code); + if (m1 == NULL) { + wps_deinit(wps); + return NULL; + } + + peer->wps = wps; + + return m1; +} + + +static struct wpabuf * +hostapd_rx_req_put_message(void *priv, struct upnp_wps_peer *peer, + const struct wpabuf *msg) +{ + enum wps_process_res res; + enum wsc_op_code op_code; + + /* PutMessage: msg = InMessage, return OutMessage */ + res = wps_process_msg(peer->wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) + return NULL; + return wps_get_msg(peer->wps, &op_code); +} + + +static struct wpabuf * +hostapd_rx_req_get_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return NULL; +} + + +static int hostapd_rx_req_set_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_del_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static struct wpabuf * +hostapd_rx_req_get_sta_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return NULL; +} + + +static int hostapd_rx_req_set_sta_settings(void *priv, + const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_del_sta_settings(void *priv, + const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_put_wlan_response( + void *priv, enum upnp_wps_wlanevent_type ev_type, + const u8 *mac_addr, const struct wpabuf *msg, + enum wps_msg_type msg_type) +{ + struct hostapd_data *hapd = priv; + struct sta_info *sta; + struct upnp_pending_message *p; + + wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" + MACSTR, ev_type, MAC2STR(mac_addr)); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", + wpabuf_head(msg), wpabuf_len(msg)); + if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " + "PutWLANResponse WLANEventType %d", ev_type); + return -1; + } + + /* + * EAP response to ongoing to WPS Registration. Send it to EAP-WSC + * server implementation for delivery to the peer. + */ + + sta = ap_get_sta(hapd, mac_addr); + if (!sta) { + /* + * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: + * Pick STA that is in an ongoing WPS registration without + * checking the MAC address. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " + "on NewWLANEventMAC; try wildcard match"); + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) + break; + } + } + + if (!sta) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); + return 0; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, sta->addr, ETH_ALEN); + p->msg = wpabuf_dup(msg); + p->type = msg_type; + p->next = hapd->wps->upnp_msgs; + hapd->wps->upnp_msgs = p; + + return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); +} + + +static int hostapd_rx_req_set_selected_registrar(void *priv, + const struct wpabuf *msg) +{ + struct hostapd_data *hapd = priv; + return wps_registrar_set_selected_registrar(hapd->wps->registrar, msg); +} + + +static int hostapd_rx_req_reboot_ap(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reset_ap(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reboot_sta(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reset_sta(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps) +{ + struct upnp_wps_device_ctx *ctx; + + if (!hapd->conf->upnp_iface) + return 0; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->rx_req_get_device_info = hostapd_rx_req_get_device_info; + ctx->rx_req_put_message = hostapd_rx_req_put_message; + ctx->rx_req_get_ap_settings = hostapd_rx_req_get_ap_settings; + ctx->rx_req_set_ap_settings = hostapd_rx_req_set_ap_settings; + ctx->rx_req_del_ap_settings = hostapd_rx_req_del_ap_settings; + ctx->rx_req_get_sta_settings = hostapd_rx_req_get_sta_settings; + ctx->rx_req_set_sta_settings = hostapd_rx_req_set_sta_settings; + ctx->rx_req_del_sta_settings = hostapd_rx_req_del_sta_settings; + ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; + ctx->rx_req_set_selected_registrar = + hostapd_rx_req_set_selected_registrar; + ctx->rx_req_reboot_ap = hostapd_rx_req_reboot_ap; + ctx->rx_req_reset_ap = hostapd_rx_req_reset_ap; + ctx->rx_req_reboot_sta = hostapd_rx_req_reboot_sta; + ctx->rx_req_reset_sta = hostapd_rx_req_reset_sta; + + hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); + if (hapd->wps_upnp == NULL) { + os_free(ctx); + return -1; + } + wps->wps_upnp = hapd->wps_upnp; + + if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { + upnp_wps_device_deinit(hapd->wps_upnp); + hapd->wps_upnp = NULL; + return -1; + } + + return 0; +} + + +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) +{ + upnp_wps_device_deinit(hapd->wps_upnp); +} + +#endif /* CONFIG_WPS_UPNP */ diff --git a/hostapd/wps_hostapd.h b/hostapd/wps_hostapd.h new file mode 100644 index 0000000000000..6615c62a177ea --- /dev/null +++ b/hostapd/wps_hostapd.h @@ -0,0 +1,48 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_HOSTAPD_H +#define WPS_HOSTAPD_H + +#ifdef CONFIG_WPS + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf); +void hostapd_deinit_wps(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin); +int hostapd_wps_button_pushed(struct hostapd_data *hapd); +void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ie_len); + +#else /* CONFIG_WPS */ + +static inline int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + return 0; +} + +static inline void hostapd_deinit_wps(struct hostapd_data *hapd) +{ +} + +static inline void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, + const u8 *addr, + const u8 *ie, size_t ie_len) +{ +} +#endif /* CONFIG_WPS */ + +#endif /* WPS_HOSTAPD_H */ diff --git a/patches/openssl-0.9.8-tls-extensions.patch b/patches/openssl-0.9.8-tls-extensions.patch new file mode 100644 index 0000000000000..44490cca226e4 --- /dev/null +++ b/patches/openssl-0.9.8-tls-extensions.patch @@ -0,0 +1,429 @@ +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). + + + +diff -uprN openssl-0.9.8.orig/include/openssl/ssl.h openssl-0.9.8/include/openssl/ssl.h +--- openssl-0.9.8.orig/include/openssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/include/openssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 +@@ -340,6 +340,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -968,6 +971,15 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; + }; + + #ifdef __cplusplus +@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8.orig/include/openssl/tls1.h openssl-0.9.8/include/openssl/tls1.h +--- openssl-0.9.8.orig/include/openssl/tls1.h 2003-07-22 05:34:21.000000000 -0700 ++++ openssl-0.9.8/include/openssl/tls1.h 2005-07-19 20:02:15.000000000 -0700 +@@ -282,6 +282,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8.orig/ssl/Makefile openssl-0.9.8/ssl/Makefile +--- openssl-0.9.8.orig/ssl/Makefile 2005-05-30 16:20:30.000000000 -0700 ++++ openssl-0.9.8/ssl/Makefile 2005-07-19 20:02:15.000000000 -0700 +@@ -24,7 +24,7 @@ LIBSRC= \ + s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ + s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ + s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ +- t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ ++ t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ + d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ + d1_both.c d1_enc.c \ + ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ +@@ -35,7 +35,7 @@ LIBOBJ= \ + s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ + s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ + s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ +- t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ ++ t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ + d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ + d1_both.o d1_enc.o \ + ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ +@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. + t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h + t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h + t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c ++t1_ext.o: t1_ext.c ssl_locl.h +diff -uprN openssl-0.9.8.orig/ssl/s3_clnt.c openssl-0.9.8/ssl/s3_clnt.c +--- openssl-0.9.8.orig/ssl/s3_clnt.c 2005-05-16 03:11:03.000000000 -0700 ++++ openssl-0.9.8/ssl/s3_clnt.c 2005-07-19 20:02:15.000000000 -0700 +@@ -606,6 +606,20 @@ int ssl3_client_hello(SSL *s) + } + *(p++)=0; /* Add the NULL method */ + ++ /* send client hello extensions if any */ ++ if (s->version >= TLS1_VERSION && s->tls_extension) ++ { ++ // set the total extensions length ++ s2n(s->tls_extension->length + 4, p); ++ ++ // put the extensions with type and length ++ s2n(s->tls_extension->type, p); ++ s2n(s->tls_extension->length, p); ++ ++ memcpy(p, s->tls_extension->data, s->tls_extension->length); ++ p+=s->tls_extension->length; ++ } ++ + l=(p-d); + d=buf; + *(d++)=SSL3_MT_CLIENT_HELLO; +@@ -628,7 +642,7 @@ int ssl3_get_server_hello(SSL *s) + STACK_OF(SSL_CIPHER) *sk; + SSL_CIPHER *c; + unsigned char *p,*d; +- int i,al,ok; ++ int i,al,ok,pre_shared; + unsigned int j; + long n; + SSL_COMP *comp; +@@ -693,7 +707,24 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + +- if (j != 0 && j == s->session->session_id_length ++ /* check if we want to resume the session based on external pre-shared secret */ ++ pre_shared = 0; ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ s->session->session_id_length = j; ++ memcpy(s->session->session_id, p, j); ++ pre_shared = 1; ++ } ++ } ++ ++ if ((pre_shared || j != 0) && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { + if(s->sid_ctx_length != s->session->sid_ctx_length +diff -uprN openssl-0.9.8.orig/ssl/s3_srvr.c openssl-0.9.8/ssl/s3_srvr.c +--- openssl-0.9.8.orig/ssl/s3_srvr.c 2005-05-22 17:32:55.000000000 -0700 ++++ openssl-0.9.8/ssl/s3_srvr.c 2005-07-19 20:02:15.000000000 -0700 +@@ -955,6 +955,75 @@ int ssl3_get_client_hello(SSL *s) + } + #endif + ++ /* Check for TLS client hello extension here */ ++ if (p < (d+n) && s->version >= TLS1_VERSION) ++ { ++ if (s->tls_extension_cb) ++ { ++ TLS_EXTENSION tls_ext; ++ unsigned short ext_total_len; ++ ++ n2s(p, ext_total_len); ++ n2s(p, tls_ext.type); ++ n2s(p, tls_ext.length); ++ ++ // sanity check in TLS extension len ++ if (tls_ext.length > (d+n) - p) ++ { ++ // just cut the lenth to packet border ++ tls_ext.length = (d+n) - p; ++ } ++ ++ tls_ext.data = p; ++ ++ // returns an alert code or 0 ++ al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); ++ if (al != 0) ++ { ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); ++ goto f_err; ++ } ++ } ++ } ++ ++ /* Check if we want to use external pre-shared secret for this handshake */ ++ /* for not reused session only */ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } ++ + /* Given s->session->ciphers and SSL_get_ciphers, we must + * pick a cipher */ + +diff -uprN openssl-0.9.8.orig/ssl/ssl_err.c openssl-0.9.8/ssl/ssl_err.c +--- openssl-0.9.8.orig/ssl/ssl_err.c 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl_err.c 2005-07-19 20:02:15.000000000 -0700 +@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -uprN openssl-0.9.8.orig/ssl/ssl.h openssl-0.9.8/ssl/ssl.h +--- openssl-0.9.8.orig/ssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 +@@ -340,6 +340,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -968,6 +971,15 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; + }; + + #ifdef __cplusplus +@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8.orig/ssl/ssl_sess.c openssl-0.9.8/ssl/ssl_sess.c +--- openssl-0.9.8.orig/ssl/ssl_sess.c 2005-04-29 13:10:06.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl_sess.c 2005-07-19 20:02:15.000000000 -0700 +@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -uprN openssl-0.9.8.orig/ssl/t1_ext.c openssl-0.9.8/ssl/t1_ext.c +--- openssl-0.9.8.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 ++++ openssl-0.9.8/ssl/t1_ext.c 2005-07-19 20:03:29.000000000 -0700 +@@ -0,0 +1,48 @@ ++ ++#include <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/patches/openssl-0.9.8d-tls-extensions.patch b/patches/openssl-0.9.8d-tls-extensions.patch new file mode 100644 index 0000000000000..eec6db8a13410 --- /dev/null +++ b/patches/openssl-0.9.8d-tls-extensions.patch @@ -0,0 +1,429 @@ +This patch is adding support for TLS hello extensions and externally +generated pre-shared key material to OpenSSL 0.9.8d. This is +based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + + + +diff -uprN openssl-0.9.8d.orig/include/openssl/ssl.h openssl-0.9.8d/include/openssl/ssl.h +--- openssl-0.9.8d.orig/include/openssl/ssl.h 2006-06-14 06:52:49.000000000 -0700 ++++ openssl-0.9.8d/include/openssl/ssl.h 2006-12-10 08:20:02.000000000 -0800 +@@ -345,6 +345,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -973,6 +976,15 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; + }; + + #ifdef __cplusplus +@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8d.orig/include/openssl/tls1.h openssl-0.9.8d/include/openssl/tls1.h +--- openssl-0.9.8d.orig/include/openssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 ++++ openssl-0.9.8d/include/openssl/tls1.h 2006-12-10 08:20:02.000000000 -0800 +@@ -296,6 +296,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8d.orig/ssl/Makefile openssl-0.9.8d/ssl/Makefile +--- openssl-0.9.8d.orig/ssl/Makefile 2006-02-03 17:49:35.000000000 -0800 ++++ openssl-0.9.8d/ssl/Makefile 2006-12-10 08:20:02.000000000 -0800 +@@ -24,7 +24,7 @@ LIBSRC= \ + s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ + s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ + s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ +- t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ ++ t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ + d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ + d1_both.c d1_enc.c \ + ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ +@@ -35,7 +35,7 @@ LIBOBJ= \ + s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ + s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ + s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ +- t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ ++ t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ + d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ + d1_both.o d1_enc.o \ + ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ +@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. + t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h + t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h + t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c ++t1_ext.o: t1_ext.c ssl_locl.h +diff -uprN openssl-0.9.8d.orig/ssl/s3_clnt.c openssl-0.9.8d/ssl/s3_clnt.c +--- openssl-0.9.8d.orig/ssl/s3_clnt.c 2005-12-12 23:41:46.000000000 -0800 ++++ openssl-0.9.8d/ssl/s3_clnt.c 2006-12-10 08:20:02.000000000 -0800 +@@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s) + #endif + *(p++)=0; /* Add the NULL method */ + ++ /* send client hello extensions if any */ ++ if (s->version >= TLS1_VERSION && s->tls_extension) ++ { ++ // set the total extensions length ++ s2n(s->tls_extension->length + 4, p); ++ ++ // put the extensions with type and length ++ s2n(s->tls_extension->type, p); ++ s2n(s->tls_extension->length, p); ++ ++ memcpy(p, s->tls_extension->data, s->tls_extension->length); ++ p+=s->tls_extension->length; ++ } ++ + l=(p-d); + d=buf; + *(d++)=SSL3_MT_CLIENT_HELLO; +@@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s) + STACK_OF(SSL_CIPHER) *sk; + SSL_CIPHER *c; + unsigned char *p,*d; +- int i,al,ok; ++ int i,al,ok,pre_shared; + unsigned int j; + long n; + #ifndef OPENSSL_NO_COMP +@@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + +- if (j != 0 && j == s->session->session_id_length ++ /* check if we want to resume the session based on external pre-shared secret */ ++ pre_shared = 0; ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ s->session->session_id_length = j; ++ memcpy(s->session->session_id, p, j); ++ pre_shared = 1; ++ } ++ } ++ ++ if ((pre_shared || j != 0) && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { + if(s->sid_ctx_length != s->session->sid_ctx_length +diff -uprN openssl-0.9.8d.orig/ssl/s3_srvr.c openssl-0.9.8d/ssl/s3_srvr.c +--- openssl-0.9.8d.orig/ssl/s3_srvr.c 2006-09-28 04:29:03.000000000 -0700 ++++ openssl-0.9.8d/ssl/s3_srvr.c 2006-12-10 08:20:02.000000000 -0800 +@@ -943,6 +943,75 @@ int ssl3_get_client_hello(SSL *s) + } + #endif + ++ /* Check for TLS client hello extension here */ ++ if (p < (d+n) && s->version >= TLS1_VERSION) ++ { ++ if (s->tls_extension_cb) ++ { ++ TLS_EXTENSION tls_ext; ++ unsigned short ext_total_len; ++ ++ n2s(p, ext_total_len); ++ n2s(p, tls_ext.type); ++ n2s(p, tls_ext.length); ++ ++ // sanity check in TLS extension len ++ if (tls_ext.length > (d+n) - p) ++ { ++ // just cut the lenth to packet border ++ tls_ext.length = (d+n) - p; ++ } ++ ++ tls_ext.data = p; ++ ++ // returns an alert code or 0 ++ al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); ++ if (al != 0) ++ { ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); ++ goto f_err; ++ } ++ } ++ } ++ ++ /* Check if we want to use external pre-shared secret for this handshake */ ++ /* for not reused session only */ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } ++ + /* Given s->session->ciphers and SSL_get_ciphers, we must + * pick a cipher */ + +diff -uprN openssl-0.9.8d.orig/ssl/ssl.h openssl-0.9.8d/ssl/ssl.h +--- openssl-0.9.8d.orig/ssl/ssl.h 2006-06-14 06:52:49.000000000 -0700 ++++ openssl-0.9.8d/ssl/ssl.h 2006-12-10 08:20:02.000000000 -0800 +@@ -345,6 +345,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -973,6 +976,15 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; + }; + + #ifdef __cplusplus +@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8d.orig/ssl/ssl_err.c openssl-0.9.8d/ssl/ssl_err.c +--- openssl-0.9.8d.orig/ssl/ssl_err.c 2006-01-08 13:52:46.000000000 -0800 ++++ openssl-0.9.8d/ssl/ssl_err.c 2006-12-10 08:20:02.000000000 -0800 +@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -uprN openssl-0.9.8d.orig/ssl/ssl_sess.c openssl-0.9.8d/ssl/ssl_sess.c +--- openssl-0.9.8d.orig/ssl/ssl_sess.c 2005-12-30 15:51:57.000000000 -0800 ++++ openssl-0.9.8d/ssl/ssl_sess.c 2006-12-10 08:20:02.000000000 -0800 +@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -uprN openssl-0.9.8d.orig/ssl/t1_ext.c openssl-0.9.8d/ssl/t1_ext.c +--- openssl-0.9.8d.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 ++++ openssl-0.9.8d/ssl/t1_ext.c 2006-12-10 08:20:02.000000000 -0800 +@@ -0,0 +1,48 @@ ++ ++#include <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.8d.orig/ssl/t1_lib.c openssl-0.9.8d/ssl/t1_lib.c +--- openssl-0.9.8d.orig/ssl/t1_lib.c 2005-08-05 16:52:07.000000000 -0700 ++++ openssl-0.9.8d/ssl/t1_lib.c 2006-12-10 08:20:02.000000000 -0800 +@@ -97,6 +97,10 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ } + ssl3_free(s); + } + +diff -uprN openssl-0.9.8d.orig/ssl/tls1.h openssl-0.9.8d/ssl/tls1.h +--- openssl-0.9.8d.orig/ssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 ++++ openssl-0.9.8d/ssl/tls1.h 2006-12-10 08:20:02.000000000 -0800 +@@ -296,6 +296,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8d.orig/util/ssleay.num openssl-0.9.8d/util/ssleay.num +--- openssl-0.9.8d.orig/util/ssleay.num 2005-05-08 17:22:02.000000000 -0700 ++++ openssl-0.9.8d/util/ssleay.num 2006-12-10 08:20:02.000000000 -0800 +@@ -226,3 +226,6 @@ DTLSv1_server_method + SSL_COMP_get_compression_methods 276 EXIST:!VMS:FUNCTION:COMP + SSL_COMP_get_compress_methods 276 EXIST:VMS:FUNCTION:COMP + SSL_SESSION_get_id 277 EXIST::FUNCTION: ++SSL_set_hello_extension 278 EXIST::FUNCTION: ++SSL_set_hello_extension_cb 279 EXIST::FUNCTION: ++SSL_set_session_secret_cb 280 EXIST::FUNCTION: diff --git a/patches/openssl-0.9.8e-tls-extensions.patch b/patches/openssl-0.9.8e-tls-extensions.patch new file mode 100644 index 0000000000000..ede053f779b6f --- /dev/null +++ b/patches/openssl-0.9.8e-tls-extensions.patch @@ -0,0 +1,353 @@ +This patch is adding support for TLS hello extensions and externally +generated pre-shared key material to OpenSSL 0.9.8e. This is +based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + + + +diff -uprN openssl-0.9.8e.orig/ssl/Makefile openssl-0.9.8e/ssl/Makefile +--- openssl-0.9.8e.orig/ssl/Makefile 2006-02-03 17:49:35.000000000 -0800 ++++ openssl-0.9.8e/ssl/Makefile 2007-03-22 20:23:19.000000000 -0700 +@@ -24,7 +24,7 @@ LIBSRC= \ + s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ + s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ + s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ +- t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ ++ t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ + d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ + d1_both.c d1_enc.c \ + ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ +@@ -35,7 +35,7 @@ LIBOBJ= \ + s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ + s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ + s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ +- t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ ++ t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ + d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ + d1_both.o d1_enc.o \ + ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ +@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. + t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h + t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h + t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c ++t1_ext.o: t1_ext.c ssl_locl.h +diff -uprN openssl-0.9.8e.orig/ssl/s3_clnt.c openssl-0.9.8e/ssl/s3_clnt.c +--- openssl-0.9.8e.orig/ssl/s3_clnt.c 2006-09-28 05:23:15.000000000 -0700 ++++ openssl-0.9.8e/ssl/s3_clnt.c 2007-03-22 20:23:19.000000000 -0700 +@@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s) + #endif + *(p++)=0; /* Add the NULL method */ + ++ /* send client hello extensions if any */ ++ if (s->version >= TLS1_VERSION && s->tls_extension) ++ { ++ // set the total extensions length ++ s2n(s->tls_extension->length + 4, p); ++ ++ // put the extensions with type and length ++ s2n(s->tls_extension->type, p); ++ s2n(s->tls_extension->length, p); ++ ++ memcpy(p, s->tls_extension->data, s->tls_extension->length); ++ p+=s->tls_extension->length; ++ } ++ + l=(p-d); + d=buf; + *(d++)=SSL3_MT_CLIENT_HELLO; +@@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s) + STACK_OF(SSL_CIPHER) *sk; + SSL_CIPHER *c; + unsigned char *p,*d; +- int i,al,ok; ++ int i,al,ok,pre_shared; + unsigned int j; + long n; + #ifndef OPENSSL_NO_COMP +@@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + +- if (j != 0 && j == s->session->session_id_length ++ /* check if we want to resume the session based on external pre-shared secret */ ++ pre_shared = 0; ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ s->session->session_id_length = j; ++ memcpy(s->session->session_id, p, j); ++ pre_shared = 1; ++ } ++ } ++ ++ if ((pre_shared || j != 0) && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { + if(s->sid_ctx_length != s->session->sid_ctx_length +diff -uprN openssl-0.9.8e.orig/ssl/s3_srvr.c openssl-0.9.8e/ssl/s3_srvr.c +--- openssl-0.9.8e.orig/ssl/s3_srvr.c 2007-02-07 12:36:40.000000000 -0800 ++++ openssl-0.9.8e/ssl/s3_srvr.c 2007-03-22 20:23:19.000000000 -0700 +@@ -945,6 +945,75 @@ int ssl3_get_client_hello(SSL *s) + } + #endif + ++ /* Check for TLS client hello extension here */ ++ if (p < (d+n) && s->version >= TLS1_VERSION) ++ { ++ if (s->tls_extension_cb) ++ { ++ TLS_EXTENSION tls_ext; ++ unsigned short ext_total_len; ++ ++ n2s(p, ext_total_len); ++ n2s(p, tls_ext.type); ++ n2s(p, tls_ext.length); ++ ++ // sanity check in TLS extension len ++ if (tls_ext.length > (d+n) - p) ++ { ++ // just cut the lenth to packet border ++ tls_ext.length = (d+n) - p; ++ } ++ ++ tls_ext.data = p; ++ ++ // returns an alert code or 0 ++ al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); ++ if (al != 0) ++ { ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); ++ goto f_err; ++ } ++ } ++ } ++ ++ /* Check if we want to use external pre-shared secret for this handshake */ ++ /* for not reused session only */ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } ++ + /* Given s->session->ciphers and SSL_get_ciphers, we must + * pick a cipher */ + +diff -uprN openssl-0.9.8e.orig/ssl/ssl.h openssl-0.9.8e/ssl/ssl.h +--- openssl-0.9.8e.orig/ssl/ssl.h 2007-02-19 09:55:07.000000000 -0800 ++++ openssl-0.9.8e/ssl/ssl.h 2007-03-22 20:23:19.000000000 -0700 +@@ -345,6 +345,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -973,6 +976,15 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; + }; + + #ifdef __cplusplus +@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8e.orig/ssl/ssl_err.c openssl-0.9.8e/ssl/ssl_err.c +--- openssl-0.9.8e.orig/ssl/ssl_err.c 2006-11-21 12:14:46.000000000 -0800 ++++ openssl-0.9.8e/ssl/ssl_err.c 2007-03-22 20:23:19.000000000 -0700 +@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -uprN openssl-0.9.8e.orig/ssl/ssl_sess.c openssl-0.9.8e/ssl/ssl_sess.c +--- openssl-0.9.8e.orig/ssl/ssl_sess.c 2007-02-10 02:40:24.000000000 -0800 ++++ openssl-0.9.8e/ssl/ssl_sess.c 2007-03-22 20:23:19.000000000 -0700 +@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -uprN openssl-0.9.8e.orig/ssl/t1_ext.c openssl-0.9.8e/ssl/t1_ext.c +--- openssl-0.9.8e.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 ++++ openssl-0.9.8e/ssl/t1_ext.c 2007-03-22 20:23:19.000000000 -0700 +@@ -0,0 +1,48 @@ ++ ++#include <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.8e.orig/ssl/t1_lib.c openssl-0.9.8e/ssl/t1_lib.c +--- openssl-0.9.8e.orig/ssl/t1_lib.c 2007-01-21 08:07:25.000000000 -0800 ++++ openssl-0.9.8e/ssl/t1_lib.c 2007-03-22 20:23:19.000000000 -0700 +@@ -97,6 +97,10 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ } + ssl3_free(s); + } + +diff -uprN openssl-0.9.8e.orig/ssl/tls1.h openssl-0.9.8e/ssl/tls1.h +--- openssl-0.9.8e.orig/ssl/tls1.h 2006-06-14 10:52:01.000000000 -0700 ++++ openssl-0.9.8e/ssl/tls1.h 2007-03-22 20:23:19.000000000 -0700 +@@ -296,6 +296,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8e.orig/util/ssleay.num openssl-0.9.8e/util/ssleay.num +--- openssl-0.9.8e.orig/util/ssleay.num 2006-11-30 05:04:43.000000000 -0800 ++++ openssl-0.9.8e/util/ssleay.num 2007-03-22 20:24:07.000000000 -0700 +@@ -238,3 +238,6 @@ SSL_CTX_set_info_callback + SSL_CTX_sess_get_new_cb 287 EXIST::FUNCTION: + SSL_CTX_get_client_cert_cb 288 EXIST::FUNCTION: + SSL_CTX_sess_get_remove_cb 289 EXIST::FUNCTION: ++SSL_set_hello_extension 290 EXIST::FUNCTION: ++SSL_set_hello_extension_cb 291 EXIST::FUNCTION: ++SSL_set_session_secret_cb 292 EXIST::FUNCTION: diff --git a/patches/openssl-0.9.8g-tls-extensions.patch b/patches/openssl-0.9.8g-tls-extensions.patch new file mode 100644 index 0000000000000..8ccbfaa29ddf6 --- /dev/null +++ b/patches/openssl-0.9.8g-tls-extensions.patch @@ -0,0 +1,330 @@ +This patch adds support for TLS SessionTicket extension (RFC 5077) for +the parts used by EAP-FAST (RFC 4851). + +This is based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +OpenSSL 0.9.8g does not enable TLS extension support by default, so it +will need to be enabled by adding enable-tlsext to config script +command line. + + +diff -upr openssl-0.9.8g.orig/ssl/s3_clnt.c openssl-0.9.8g/ssl/s3_clnt.c +--- openssl-0.9.8g.orig/ssl/s3_clnt.c 2007-08-31 03:28:51.000000000 +0300 ++++ openssl-0.9.8g/ssl/s3_clnt.c 2008-04-15 17:11:46.000000000 +0300 +@@ -727,6 +727,20 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + ++#ifndef OPENSSL_NO_TLSEXT ++ /* check if we want to resume the session based on external pre-shared secret */ ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { +diff -upr openssl-0.9.8g.orig/ssl/s3_srvr.c openssl-0.9.8g/ssl/s3_srvr.c +--- openssl-0.9.8g.orig/ssl/s3_srvr.c 2007-09-30 21:55:59.000000000 +0300 ++++ openssl-0.9.8g/ssl/s3_srvr.c 2008-04-15 17:10:37.000000000 +0300 +@@ -928,6 +928,59 @@ int ssl3_get_client_hello(SSL *s) + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } ++ ++ /* Check if we want to use external pre-shared secret for this ++ * handshake for not reused session only. We need to generate ++ * server_random before calling tls_session_secret_cb in order to allow ++ * SessionTicket processing to use it in key derivation. */ ++ { ++ unsigned long Time; ++ unsigned char *pos; ++ Time=(unsigned long)time(NULL); /* Time */ ++ pos=s->s3->server_random; ++ l2n(Time,pos); ++ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) ++ { ++ al=SSL_AD_INTERNAL_ERROR; ++ goto f_err; ++ } ++ } ++ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } + #endif + /* Worst case, we will use the NULL compression, but if we have other + * options, we will now look for them. We have i-1 compression +@@ -1066,16 +1119,22 @@ int ssl3_send_server_hello(SSL *s) + unsigned char *buf; + unsigned char *p,*d; + int i,sl; +- unsigned long l,Time; ++ unsigned long l; ++#ifdef OPENSSL_NO_TLSEXT ++ unsigned long Time; ++#endif + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) + { + buf=(unsigned char *)s->init_buf->data; ++#ifdef OPENSSL_NO_TLSEXT + p=s->s3->server_random; ++ /* Generate server_random if it was not needed previously */ + Time=(unsigned long)time(NULL); /* Time */ + l2n(Time,p); + if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) + return -1; ++#endif + /* Do the message type and length last */ + d=p= &(buf[4]); + +diff -upr openssl-0.9.8g.orig/ssl/ssl.h openssl-0.9.8g/ssl/ssl.h +--- openssl-0.9.8g.orig/ssl/ssl.h 2007-10-19 10:42:38.000000000 +0300 ++++ openssl-0.9.8g/ssl/ssl.h 2008-04-15 17:10:37.000000000 +0300 +@@ -342,6 +342,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -363,6 +364,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -1004,6 +1007,14 @@ struct ssl_st + */ + /* RFC4507 session ticket expected to be received or sent */ + int tlsext_ticket_expected; ++ ++ /* TLS extensions */ ++ TLS_EXTENSION *tls_extension; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; ++ + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ + #define session_ctx initial_ctx + #else +@@ -1589,6 +1600,12 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1778,6 +1795,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -upr openssl-0.9.8g.orig/ssl/ssl_err.c openssl-0.9.8g/ssl/ssl_err.c +--- openssl-0.9.8g.orig/ssl/ssl_err.c 2007-10-11 17:36:59.000000000 +0300 ++++ openssl-0.9.8g/ssl/ssl_err.c 2008-04-15 17:10:37.000000000 +0300 +@@ -250,6 +250,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -upr openssl-0.9.8g.orig/ssl/ssl_sess.c openssl-0.9.8g/ssl/ssl_sess.c +--- openssl-0.9.8g.orig/ssl/ssl_sess.c 2007-10-19 10:36:34.000000000 +0300 ++++ openssl-0.9.8g/ssl/ssl_sess.c 2008-04-15 17:10:37.000000000 +0300 +@@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++#ifndef OPENSSL_NO_TLSEXT ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) ++{ ++ if(s->version >= TLS1_VERSION) ++ { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ s->tls_extension = NULL; ++ } ++ ++ s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); ++ if(!s->tls_extension) ++ { ++ SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ s->tls_extension->type = ext_type; ++ ++ if(ext_data) ++ { ++ s->tls_extension->length = ext_len; ++ s->tls_extension->data = s->tls_extension + 1; ++ memcpy(s->tls_extension->data, ext_data, ext_len); ++ } else { ++ s->tls_extension->length = 0; ++ s->tls_extension->data = NULL; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++#endif /* OPENSSL_NO_TLSEXT */ ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -upr openssl-0.9.8g.orig/ssl/t1_lib.c openssl-0.9.8g/ssl/t1_lib.c +--- openssl-0.9.8g.orig/ssl/t1_lib.c 2007-10-19 10:44:10.000000000 +0300 ++++ openssl-0.9.8g/ssl/t1_lib.c 2008-04-15 17:10:37.000000000 +0300 +@@ -105,6 +105,12 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++#ifndef OPENSSL_NO_TLSEXT ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ } ++#endif + ssl3_free(s); + } + +@@ -174,8 +180,24 @@ unsigned char *ssl_add_clienthello_tlsex + int ticklen; + if (s->session && s->session->tlsext_tick) + ticklen = s->session->tlsext_ticklen; ++ else if (s->session && s->tls_extension && ++ s->tls_extension->type == TLSEXT_TYPE_session_ticket && ++ s->tls_extension->data) ++ { ++ ticklen = s->tls_extension->length; ++ s->session->tlsext_tick = OPENSSL_malloc(ticklen); ++ if (!s->session->tlsext_tick) ++ return NULL; ++ memcpy(s->session->tlsext_tick, s->tls_extension->data, ++ ticklen); ++ s->session->tlsext_ticklen = ticklen; ++ } + else + ticklen = 0; ++ if (ticklen == 0 && s->tls_extension && ++ s->tls_extension->type == TLSEXT_TYPE_session_ticket && ++ s->tls_extension->data == NULL) ++ goto skip_ext; + /* Check for enough room 2 for extension type, 2 for len + * rest for ticket + */ +@@ -189,6 +211,7 @@ unsigned char *ssl_add_clienthello_tlsex + ret += ticklen; + } + } ++ skip_ext: + + if ((extdatalen = ret-p-2)== 0) + return p; +@@ -543,6 +566,8 @@ int tls1_process_ticket(SSL *s, unsigned + s->tlsext_ticket_expected = 1; + return 0; /* Cache miss */ + } ++ if (s->tls_session_secret_cb) ++ return 0; + return tls_decrypt_ticket(s, p, size, session_id, len, + ret); + } +diff -upr openssl-0.9.8g.orig/ssl/tls1.h openssl-0.9.8g/ssl/tls1.h +--- openssl-0.9.8g.orig/ssl/tls1.h 2007-08-28 04:12:44.000000000 +0300 ++++ openssl-0.9.8g/ssl/tls1.h 2008-04-15 17:10:37.000000000 +0300 +@@ -365,6 +365,14 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SER + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -upr openssl-0.9.8g.orig/util/ssleay.num openssl-0.9.8g/util/ssleay.num +--- openssl-0.9.8g.orig/util/ssleay.num 2007-08-13 01:31:16.000000000 +0300 ++++ openssl-0.9.8g/util/ssleay.num 2008-04-15 17:10:37.000000000 +0300 +@@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb + SSL_set_SSL_CTX 290 EXIST::FUNCTION: + SSL_get_servername 291 EXIST::FUNCTION:TLSEXT + SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT ++SSL_set_hello_extension 305 EXIST::FUNCTION:TLSEXT ++SSL_set_session_secret_cb 306 EXIST::FUNCTION:TLSEXT diff --git a/patches/openssl-0.9.8h-tls-extensions.patch b/patches/openssl-0.9.8h-tls-extensions.patch new file mode 100644 index 0000000000000..c68f2279b7622 --- /dev/null +++ b/patches/openssl-0.9.8h-tls-extensions.patch @@ -0,0 +1,344 @@ +This patch adds support for TLS SessionTicket extension (RFC 5077) for +the parts used by EAP-FAST (RFC 4851). + +This is based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +OpenSSL 0.9.8h does not enable TLS extension support by default, so it +will need to be enabled by adding enable-tlsext to config script +command line. + + +diff -upr openssl-0.9.8h.orig/ssl/s3_clnt.c openssl-0.9.8h/ssl/s3_clnt.c +--- openssl-0.9.8h.orig/ssl/s3_clnt.c 2008-05-28 10:29:27.000000000 +0300 ++++ openssl-0.9.8h/ssl/s3_clnt.c 2008-05-29 10:44:25.000000000 +0300 +@@ -752,6 +752,20 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + ++#ifndef OPENSSL_NO_TLSEXT ++ /* check if we want to resume the session based on external pre-shared secret */ ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { +@@ -2693,11 +2707,8 @@ static int ssl3_check_finished(SSL *s) + { + int ok; + long n; +- /* If we have no ticket or session ID is non-zero length (a match of +- * a non-zero session length would never reach here) it cannot be a +- * resumed session. +- */ +- if (!s->session->tlsext_tick || s->session->session_id_length) ++ /* If we have no ticket it cannot be a resumed session. */ ++ if (!s->session->tlsext_tick) + return 1; + /* this function is called when we really expect a Certificate + * message, so permit appropriate message length */ +diff -upr openssl-0.9.8h.orig/ssl/s3_srvr.c openssl-0.9.8h/ssl/s3_srvr.c +--- openssl-0.9.8h.orig/ssl/s3_srvr.c 2008-04-30 19:11:32.000000000 +0300 ++++ openssl-0.9.8h/ssl/s3_srvr.c 2008-05-28 18:49:34.000000000 +0300 +@@ -959,6 +959,59 @@ int ssl3_get_client_hello(SSL *s) + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } ++ ++ /* Check if we want to use external pre-shared secret for this ++ * handshake for not reused session only. We need to generate ++ * server_random before calling tls_session_secret_cb in order to allow ++ * SessionTicket processing to use it in key derivation. */ ++ { ++ unsigned long Time; ++ unsigned char *pos; ++ Time=(unsigned long)time(NULL); /* Time */ ++ pos=s->s3->server_random; ++ l2n(Time,pos); ++ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) ++ { ++ al=SSL_AD_INTERNAL_ERROR; ++ goto f_err; ++ } ++ } ++ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } + #endif + /* Worst case, we will use the NULL compression, but if we have other + * options, we will now look for them. We have i-1 compression +@@ -1097,16 +1150,22 @@ int ssl3_send_server_hello(SSL *s) + unsigned char *buf; + unsigned char *p,*d; + int i,sl; +- unsigned long l,Time; ++ unsigned long l; ++#ifdef OPENSSL_NO_TLSEXT ++ unsigned long Time; ++#endif + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) + { + buf=(unsigned char *)s->init_buf->data; ++#ifdef OPENSSL_NO_TLSEXT + p=s->s3->server_random; ++ /* Generate server_random if it was not needed previously */ + Time=(unsigned long)time(NULL); /* Time */ + l2n(Time,p); + if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) + return -1; ++#endif + /* Do the message type and length last */ + d=p= &(buf[4]); + +diff -upr openssl-0.9.8h.orig/ssl/ssl.h openssl-0.9.8h/ssl/ssl.h +--- openssl-0.9.8h.orig/ssl/ssl.h 2008-04-30 19:11:32.000000000 +0300 ++++ openssl-0.9.8h/ssl/ssl.h 2008-05-28 18:49:34.000000000 +0300 +@@ -343,6 +343,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -364,6 +365,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -1027,6 +1030,14 @@ struct ssl_st + + /* RFC4507 session ticket expected to be received or sent */ + int tlsext_ticket_expected; ++ ++ /* TLS extensions */ ++ TLS_EXTENSION *tls_extension; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; ++ + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ + #define session_ctx initial_ctx + #else +@@ -1625,6 +1636,12 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1815,6 +1832,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -upr openssl-0.9.8h.orig/ssl/ssl_err.c openssl-0.9.8h/ssl/ssl_err.c +--- openssl-0.9.8h.orig/ssl/ssl_err.c 2007-10-12 03:00:30.000000000 +0300 ++++ openssl-0.9.8h/ssl/ssl_err.c 2008-05-28 18:49:34.000000000 +0300 +@@ -251,6 +251,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -upr openssl-0.9.8h.orig/ssl/ssl_sess.c openssl-0.9.8h/ssl/ssl_sess.c +--- openssl-0.9.8h.orig/ssl/ssl_sess.c 2007-10-17 20:30:15.000000000 +0300 ++++ openssl-0.9.8h/ssl/ssl_sess.c 2008-05-28 18:49:34.000000000 +0300 +@@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++#ifndef OPENSSL_NO_TLSEXT ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) ++{ ++ if(s->version >= TLS1_VERSION) ++ { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ s->tls_extension = NULL; ++ } ++ ++ s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); ++ if(!s->tls_extension) ++ { ++ SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ s->tls_extension->type = ext_type; ++ ++ if(ext_data) ++ { ++ s->tls_extension->length = ext_len; ++ s->tls_extension->data = s->tls_extension + 1; ++ memcpy(s->tls_extension->data, ext_data, ext_len); ++ } else { ++ s->tls_extension->length = 0; ++ s->tls_extension->data = NULL; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++#endif /* OPENSSL_NO_TLSEXT */ ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -upr openssl-0.9.8h.orig/ssl/t1_lib.c openssl-0.9.8h/ssl/t1_lib.c +--- openssl-0.9.8h.orig/ssl/t1_lib.c 2008-05-28 10:26:33.000000000 +0300 ++++ openssl-0.9.8h/ssl/t1_lib.c 2008-05-28 18:49:34.000000000 +0300 +@@ -106,6 +106,12 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++#ifndef OPENSSL_NO_TLSEXT ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ } ++#endif + ssl3_free(s); + } + +@@ -175,8 +181,24 @@ unsigned char *ssl_add_clienthello_tlsex + int ticklen; + if (s->session && s->session->tlsext_tick) + ticklen = s->session->tlsext_ticklen; ++ else if (s->session && s->tls_extension && ++ s->tls_extension->type == TLSEXT_TYPE_session_ticket && ++ s->tls_extension->data) ++ { ++ ticklen = s->tls_extension->length; ++ s->session->tlsext_tick = OPENSSL_malloc(ticklen); ++ if (!s->session->tlsext_tick) ++ return NULL; ++ memcpy(s->session->tlsext_tick, s->tls_extension->data, ++ ticklen); ++ s->session->tlsext_ticklen = ticklen; ++ } + else + ticklen = 0; ++ if (ticklen == 0 && s->tls_extension && ++ s->tls_extension->type == TLSEXT_TYPE_session_ticket && ++ s->tls_extension->data == NULL) ++ goto skip_ext; + /* Check for enough room 2 for extension type, 2 for len + * rest for ticket + */ +@@ -190,6 +212,7 @@ unsigned char *ssl_add_clienthello_tlsex + ret += ticklen; + } + } ++ skip_ext: + + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) + { +@@ -774,6 +797,8 @@ int tls1_process_ticket(SSL *s, unsigned + s->tlsext_ticket_expected = 1; + return 0; /* Cache miss */ + } ++ if (s->tls_session_secret_cb) ++ return 0; + return tls_decrypt_ticket(s, p, size, session_id, len, + ret); + } +diff -upr openssl-0.9.8h.orig/ssl/tls1.h openssl-0.9.8h/ssl/tls1.h +--- openssl-0.9.8h.orig/ssl/tls1.h 2008-04-30 19:11:33.000000000 +0300 ++++ openssl-0.9.8h/ssl/tls1.h 2008-05-28 18:49:34.000000000 +0300 +@@ -398,6 +398,14 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -upr openssl-0.9.8h.orig/util/ssleay.num openssl-0.9.8h/util/ssleay.num +--- openssl-0.9.8h.orig/util/ssleay.num 2007-08-13 01:31:16.000000000 +0300 ++++ openssl-0.9.8h/util/ssleay.num 2008-05-28 18:49:34.000000000 +0300 +@@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb + SSL_set_SSL_CTX 290 EXIST::FUNCTION: + SSL_get_servername 291 EXIST::FUNCTION:TLSEXT + SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT ++SSL_set_hello_extension 305 EXIST::FUNCTION:TLSEXT ++SSL_set_session_secret_cb 306 EXIST::FUNCTION:TLSEXT diff --git a/patches/openssl-0.9.8i-tls-extensions.patch b/patches/openssl-0.9.8i-tls-extensions.patch new file mode 100644 index 0000000000000..90bff544eb59c --- /dev/null +++ b/patches/openssl-0.9.8i-tls-extensions.patch @@ -0,0 +1,404 @@ +This patch adds support for TLS SessionTicket extension (RFC 5077) for +the parts used by EAP-FAST (RFC 4851). + +This is based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +OpenSSL 0.9.8i does not enable TLS extension support by default, so it +will need to be enabled by adding enable-tlsext to config script +command line. + + +Index: openssl-0.9.8i/ssl/s3_clnt.c +=================================================================== +--- openssl-0.9.8i.orig/ssl/s3_clnt.c 2008-06-16 19:56:41.000000000 +0300 ++++ openssl-0.9.8i/ssl/s3_clnt.c 2008-11-23 20:39:40.000000000 +0200 +@@ -759,6 +759,21 @@ + goto f_err; + } + ++#ifndef OPENSSL_NO_TLSEXT ++ /* check if we want to resume the session based on external pre-shared secret */ ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->session->cipher=pref_cipher ? ++ pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { +@@ -2701,11 +2716,8 @@ + { + int ok; + long n; +- /* If we have no ticket or session ID is non-zero length (a match of +- * a non-zero session length would never reach here) it cannot be a +- * resumed session. +- */ +- if (!s->session->tlsext_tick || s->session->session_id_length) ++ /* If we have no ticket it cannot be a resumed session. */ ++ if (!s->session->tlsext_tick) + return 1; + /* this function is called when we really expect a Certificate + * message, so permit appropriate message length */ +Index: openssl-0.9.8i/ssl/s3_srvr.c +=================================================================== +--- openssl-0.9.8i.orig/ssl/s3_srvr.c 2008-09-14 21:16:09.000000000 +0300 ++++ openssl-0.9.8i/ssl/s3_srvr.c 2008-11-23 20:37:40.000000000 +0200 +@@ -959,6 +959,59 @@ + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } ++ ++ /* Check if we want to use external pre-shared secret for this ++ * handshake for not reused session only. We need to generate ++ * server_random before calling tls_session_secret_cb in order to allow ++ * SessionTicket processing to use it in key derivation. */ ++ { ++ unsigned long Time; ++ unsigned char *pos; ++ Time=(unsigned long)time(NULL); /* Time */ ++ pos=s->s3->server_random; ++ l2n(Time,pos); ++ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) ++ { ++ al=SSL_AD_INTERNAL_ERROR; ++ goto f_err; ++ } ++ } ++ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } + #endif + /* Worst case, we will use the NULL compression, but if we have other + * options, we will now look for them. We have i-1 compression +@@ -1097,16 +1150,22 @@ + unsigned char *buf; + unsigned char *p,*d; + int i,sl; +- unsigned long l,Time; ++ unsigned long l; ++#ifdef OPENSSL_NO_TLSEXT ++ unsigned long Time; ++#endif + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) + { + buf=(unsigned char *)s->init_buf->data; ++#ifdef OPENSSL_NO_TLSEXT + p=s->s3->server_random; ++ /* Generate server_random if it was not needed previously */ + Time=(unsigned long)time(NULL); /* Time */ + l2n(Time,p); + if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) + return -1; ++#endif + /* Do the message type and length last */ + d=p= &(buf[4]); + +Index: openssl-0.9.8i/ssl/ssl_err.c +=================================================================== +--- openssl-0.9.8i.orig/ssl/ssl_err.c 2008-08-13 22:44:44.000000000 +0300 ++++ openssl-0.9.8i/ssl/ssl_err.c 2008-11-23 20:33:43.000000000 +0200 +@@ -253,6 +253,7 @@ + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, + {0,NULL} + }; + +Index: openssl-0.9.8i/ssl/ssl.h +=================================================================== +--- openssl-0.9.8i.orig/ssl/ssl.h 2008-08-13 22:44:44.000000000 +0300 ++++ openssl-0.9.8i/ssl/ssl.h 2008-11-23 20:35:41.000000000 +0200 +@@ -344,6 +344,7 @@ + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -362,6 +363,9 @@ + + DECLARE_STACK_OF(SSL_CIPHER) + ++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -1034,6 +1038,18 @@ + + /* RFC4507 session ticket expected to be received or sent */ + int tlsext_ticket_expected; ++ ++ /* TLS Session Ticket extension override */ ++ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; ++ ++ /* TLS Session Ticket extension callback */ ++ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; ++ void *tls_session_ticket_ext_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; ++ + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ + #define session_ctx initial_ctx + #else +@@ -1632,6 +1648,15 @@ + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); ++ ++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, ++ void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1824,6 +1849,7 @@ + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +Index: openssl-0.9.8i/ssl/ssl_sess.c +=================================================================== +--- openssl-0.9.8i.orig/ssl/ssl_sess.c 2008-06-04 21:35:27.000000000 +0300 ++++ openssl-0.9.8i/ssl/ssl_sess.c 2008-11-23 20:32:24.000000000 +0200 +@@ -707,6 +707,61 @@ + return(s->session_timeout); + } + ++#ifndef OPENSSL_NO_TLSEXT ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++ { ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++ } ++ ++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, ++ void *arg) ++ { ++ if (s == NULL) return(0); ++ s->tls_session_ticket_ext_cb = cb; ++ s->tls_session_ticket_ext_cb_arg = arg; ++ return(1); ++ } ++ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) ++ { ++ if (s->version >= TLS1_VERSION) ++ { ++ if (s->tlsext_session_ticket) ++ { ++ OPENSSL_free(s->tlsext_session_ticket); ++ s->tlsext_session_ticket = NULL; ++ } ++ ++ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); ++ if (!s->tlsext_session_ticket) ++ { ++ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ if (ext_data) ++ { ++ s->tlsext_session_ticket->length = ext_len; ++ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; ++ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); ++ } ++ else ++ { ++ s->tlsext_session_ticket->length = 0; ++ s->tlsext_session_ticket->data = NULL; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +Index: openssl-0.9.8i/ssl/t1_lib.c +=================================================================== +--- openssl-0.9.8i.orig/ssl/t1_lib.c 2008-09-04 01:13:04.000000000 +0300 ++++ openssl-0.9.8i/ssl/t1_lib.c 2008-11-23 20:31:20.000000000 +0200 +@@ -106,6 +106,12 @@ + + void tls1_free(SSL *s) + { ++#ifndef OPENSSL_NO_TLSEXT ++ if (s->tlsext_session_ticket) ++ { ++ OPENSSL_free(s->tlsext_session_ticket); ++ } ++#endif + ssl3_free(s); + } + +@@ -175,8 +181,23 @@ + int ticklen; + if (s->session && s->session->tlsext_tick) + ticklen = s->session->tlsext_ticklen; ++ else if (s->session && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data) ++ { ++ ticklen = s->tlsext_session_ticket->length; ++ s->session->tlsext_tick = OPENSSL_malloc(ticklen); ++ if (!s->session->tlsext_tick) ++ return NULL; ++ memcpy(s->session->tlsext_tick, ++ s->tlsext_session_ticket->data, ++ ticklen); ++ s->session->tlsext_ticklen = ticklen; ++ } + else + ticklen = 0; ++ if (ticklen == 0 && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data == NULL) ++ goto skip_ext; + /* Check for enough room 2 for extension type, 2 for len + * rest for ticket + */ +@@ -190,6 +211,7 @@ + ret += ticklen; + } + } ++ skip_ext: + + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) + { +@@ -407,6 +429,15 @@ + } + + } ++ else if (type == TLSEXT_TYPE_session_ticket) ++ { ++ if (s->tls_session_ticket_ext_cb && ++ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) ++ { ++ *al = TLS1_AD_INTERNAL_ERROR; ++ return 0; ++ } ++ } + else if (type == TLSEXT_TYPE_status_request + && s->ctx->tlsext_status_cb) + { +@@ -553,6 +584,12 @@ + } + else if (type == TLSEXT_TYPE_session_ticket) + { ++ if (s->tls_session_ticket_ext_cb && ++ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) ++ { ++ *al = TLS1_AD_INTERNAL_ERROR; ++ return 0; ++ } + if ((SSL_get_options(s) & SSL_OP_NO_TICKET) + || (size > 0)) + { +@@ -776,6 +813,15 @@ + s->tlsext_ticket_expected = 1; + return 0; /* Cache miss */ + } ++ if (s->tls_session_secret_cb) ++ { ++ /* Indicate cache miss here and instead of ++ * generating the session from ticket now, ++ * trigger abbreviated handshake based on ++ * external mechanism to calculate the master ++ * secret later. */ ++ return 0; ++ } + return tls_decrypt_ticket(s, p, size, session_id, len, + ret); + } +Index: openssl-0.9.8i/ssl/tls1.h +=================================================================== +--- openssl-0.9.8i.orig/ssl/tls1.h 2008-04-30 19:11:33.000000000 +0300 ++++ openssl-0.9.8i/ssl/tls1.h 2008-11-23 20:22:38.000000000 +0200 +@@ -398,6 +398,13 @@ + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_session_ticket_ext_st ++ { ++ unsigned short length; ++ void *data; ++ }; ++ + #ifdef __cplusplus + } + #endif +Index: openssl-0.9.8i/util/ssleay.num +=================================================================== +--- openssl-0.9.8i.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300 ++++ openssl-0.9.8i/util/ssleay.num 2008-11-23 20:22:05.000000000 +0200 +@@ -242,3 +242,5 @@ + SSL_get_servername 291 EXIST::FUNCTION:TLSEXT + SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT + SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE ++SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT ++SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT diff --git a/patches/openssl-0.9.9-session-ticket.patch b/patches/openssl-0.9.9-session-ticket.patch new file mode 100644 index 0000000000000..3afa639ad8986 --- /dev/null +++ b/patches/openssl-0.9.9-session-ticket.patch @@ -0,0 +1,374 @@ +This patch adds support for TLS SessionTicket extension (RFC 5077) for +the parts used by EAP-FAST (RFC 4851). + +This is based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +NOTE: This patch (without SSL_set_hello_extension() wrapper) was +merged into the upstream OpenSSL 0.9.9 tree and as such, an external +patch for EAP-FAST support is not needed anymore. + + + +Index: openssl-SNAP-20081111/ssl/s3_clnt.c +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/s3_clnt.c ++++ openssl-SNAP-20081111/ssl/s3_clnt.c +@@ -788,6 +788,23 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + ++#ifndef OPENSSL_NO_TLSEXT ++ /* check if we want to resume the session based on external pre-shared secret */ ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, ++ &s->session->master_key_length, ++ NULL, &pref_cipher, ++ s->tls_session_secret_cb_arg)) ++ { ++ s->session->cipher = pref_cipher ? ++ pref_cipher : ssl_get_cipher_by_char(s, p+j); ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { +@@ -2927,11 +2944,8 @@ static int ssl3_check_finished(SSL *s) + { + int ok; + long n; +- /* If we have no ticket or session ID is non-zero length (a match of +- * a non-zero session length would never reach here) it cannot be a +- * resumed session. +- */ +- if (!s->session->tlsext_tick || s->session->session_id_length) ++ /* If we have no ticket it cannot be a resumed session. */ ++ if (!s->session->tlsext_tick) + return 1; + /* this function is called when we really expect a Certificate + * message, so permit appropriate message length */ +Index: openssl-SNAP-20081111/ssl/s3_srvr.c +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/s3_srvr.c ++++ openssl-SNAP-20081111/ssl/s3_srvr.c +@@ -1010,6 +1010,59 @@ int ssl3_get_client_hello(SSL *s) + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } ++ ++ /* Check if we want to use external pre-shared secret for this ++ * handshake for not reused session only. We need to generate ++ * server_random before calling tls_session_secret_cb in order to allow ++ * SessionTicket processing to use it in key derivation. */ ++ { ++ unsigned long Time; ++ unsigned char *pos; ++ Time=(unsigned long)time(NULL); /* Time */ ++ pos=s->s3->server_random; ++ l2n(Time,pos); ++ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) ++ { ++ al=SSL_AD_INTERNAL_ERROR; ++ goto f_err; ++ } ++ } ++ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) ++ { ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } + #endif + + /* Worst case, we will use the NULL compression, but if we have other +@@ -1134,16 +1187,22 @@ int ssl3_send_server_hello(SSL *s) + unsigned char *buf; + unsigned char *p,*d; + int i,sl; +- unsigned long l,Time; ++ unsigned long l; ++#ifdef OPENSSL_NO_TLSEXT ++ unsigned long Time; ++#endif + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) + { + buf=(unsigned char *)s->init_buf->data; ++#ifdef OPENSSL_NO_TLSEXT + p=s->s3->server_random; ++ /* Generate server_random if it was not needed previously */ + Time=(unsigned long)time(NULL); /* Time */ + l2n(Time,p); + if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) + return -1; ++#endif + /* Do the message type and length last */ + d=p= &(buf[4]); + +Index: openssl-SNAP-20081111/ssl/ssl_err.c +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/ssl_err.c ++++ openssl-SNAP-20081111/ssl/ssl_err.c +@@ -263,6 +263,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_PRF), "tls1_prf"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, + {0,NULL} + }; + +Index: openssl-SNAP-20081111/ssl/ssl.h +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/ssl.h ++++ openssl-SNAP-20081111/ssl/ssl.h +@@ -355,6 +355,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -378,6 +379,8 @@ typedef struct ssl_cipher_st + + DECLARE_STACK_OF(SSL_CIPHER) + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -1145,6 +1148,13 @@ struct ssl_st + void *tlsext_opaque_prf_input; + size_t tlsext_opaque_prf_input_len; + ++ /* TLS Session Ticket extension override */ ++ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; ++ + SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ + #define session_ctx initial_ctx + #else +@@ -1746,6 +1756,16 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* NOTE: This function will be removed; it is only here for backwards ++ * compatibility for the API during testing. */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++ ++/* TLS extensions functions */ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1948,6 +1968,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_PRF 284 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +Index: openssl-SNAP-20081111/ssl/ssl_sess.c +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/ssl_sess.c ++++ openssl-SNAP-20081111/ssl/ssl_sess.c +@@ -834,6 +834,62 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); + } + ++#ifndef OPENSSL_NO_TLSEXT ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++ { ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++ } ++ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) ++ { ++ if (s->version >= TLS1_VERSION) ++ { ++ if (s->tlsext_session_ticket) ++ { ++ OPENSSL_free(s->tlsext_session_ticket); ++ s->tlsext_session_ticket = NULL; ++ } ++ ++ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); ++ if (!s->tlsext_session_ticket) ++ { ++ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ if (ext_data) ++ { ++ s->tlsext_session_ticket->length = ext_len; ++ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; ++ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); ++ } ++ else ++ { ++ s->tlsext_session_ticket->length = 0; ++ s->tlsext_session_ticket->data = NULL; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++ } ++ ++/* NOTE: This function will be removed; it is only here for backwards ++ * compatibility for the API during testing. */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) ++ { ++ if (ext_type != TLSEXT_TYPE_session_ticket) ++ return 0; ++ ++ return SSL_set_session_ticket_ext(s, ext_data, ext_len); ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +Index: openssl-SNAP-20081111/ssl/t1_lib.c +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/t1_lib.c ++++ openssl-SNAP-20081111/ssl/t1_lib.c +@@ -154,6 +154,12 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++#ifndef OPENSSL_NO_TLSEXT ++ if (s->tlsext_session_ticket) ++ { ++ OPENSSL_free(s->tlsext_session_ticket); ++ } ++#endif /* OPENSSL_NO_TLSEXT */ + ssl3_free(s); + } + +@@ -357,8 +363,23 @@ unsigned char *ssl_add_clienthello_tlsex + int ticklen; + if (s->session && s->session->tlsext_tick) + ticklen = s->session->tlsext_ticklen; ++ else if (s->session && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data) ++ { ++ ticklen = s->tlsext_session_ticket->length; ++ s->session->tlsext_tick = OPENSSL_malloc(ticklen); ++ if (!s->session->tlsext_tick) ++ return NULL; ++ memcpy(s->session->tlsext_tick, ++ s->tlsext_session_ticket->data, ++ ticklen); ++ s->session->tlsext_ticklen = ticklen; ++ } + else + ticklen = 0; ++ if (ticklen == 0 && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data == NULL) ++ goto skip_ext; + /* Check for enough room 2 for extension type, 2 for len + * rest for ticket + */ +@@ -371,6 +392,7 @@ unsigned char *ssl_add_clienthello_tlsex + ret += ticklen; + } + } ++ skip_ext: + + #ifdef TLSEXT_TYPE_opaque_prf_input + if (s->s3->client_opaque_prf_input != NULL) +@@ -1435,6 +1457,15 @@ int tls1_process_ticket(SSL *s, unsigned + s->tlsext_ticket_expected = 1; + return 0; /* Cache miss */ + } ++ if (s->tls_session_secret_cb) ++ { ++ /* Indicate cache miss here and instead of ++ * generating the session from ticket now, ++ * trigger abbreviated handshake based on ++ * external mechanism to calculate the master ++ * secret later. */ ++ return 0; ++ } + return tls_decrypt_ticket(s, p, size, session_id, len, + ret); + } +Index: openssl-SNAP-20081111/ssl/tls1.h +=================================================================== +--- openssl-SNAP-20081111.orig/ssl/tls1.h ++++ openssl-SNAP-20081111/ssl/tls1.h +@@ -512,6 +512,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS Session Ticket extension struct */ ++struct tls_session_ticket_ext_st ++ { ++ unsigned short length; ++ void *data; ++ }; ++ + #ifdef __cplusplus + } + #endif +Index: openssl-SNAP-20081111/util/ssleay.num +=================================================================== +--- openssl-SNAP-20081111.orig/util/ssleay.num ++++ openssl-SNAP-20081111/util/ssleay.num +@@ -254,3 +254,5 @@ PEM_read_bio_SSL_SESSION + SSL_CTX_set_psk_server_callback 303 EXIST::FUNCTION:PSK + SSL_get_psk_identity 304 EXIST::FUNCTION:PSK + PEM_write_SSL_SESSION 305 EXIST:!WIN16:FUNCTION: ++SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT ++SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000000000..bd1c160cd514b --- /dev/null +++ b/src/Makefile @@ -0,0 +1,11 @@ +SUBDIRS=common crypto drivers hlr_auc_gw eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps + +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done + rm -f *~ + +install: + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done diff --git a/src/common/.gitignore b/src/common/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/common/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/common/Makefile b/src/common/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/common/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/defs.h b/src/common/defs.h index 603fc55c110a7..4930e73e75cc1 100644 --- a/contrib/hostapd/defs.h +++ b/src/common/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,12 +24,71 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) +#ifdef CONFIG_IEEE80211W +#define WPA_CIPHER_AES_128_CMAC BIT(5) +#endif /* CONFIG_IEEE80211W */ + +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) +#define WPA_KEY_MGMT_NONE BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) +#define WPA_KEY_MGMT_WPA_NONE BIT(4) +#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5) +#define WPA_KEY_MGMT_FT_PSK BIT(6) +#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) +#define WPA_KEY_MGMT_PSK_SHA256 BIT(8) +#define WPA_KEY_MGMT_WPS BIT(9) + +static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) +{ + return akm == WPA_KEY_MGMT_IEEE8021X || + akm == WPA_KEY_MGMT_FT_IEEE8021X || + akm == WPA_KEY_MGMT_IEEE8021X_SHA256; +} + +static inline int wpa_key_mgmt_wpa_psk(int akm) +{ + return akm == WPA_KEY_MGMT_PSK || + akm == WPA_KEY_MGMT_FT_PSK || + akm == WPA_KEY_MGMT_PSK_SHA256; +} + +static inline int wpa_key_mgmt_ft(int akm) +{ + return akm == WPA_KEY_MGMT_FT_PSK || + akm == WPA_KEY_MGMT_FT_IEEE8021X; +} + +static inline int wpa_key_mgmt_sha256(int akm) +{ + return akm == WPA_KEY_MGMT_PSK_SHA256 || + akm == WPA_KEY_MGMT_IEEE8021X_SHA256; +} + + +#define WPA_PROTO_WPA BIT(0) +#define WPA_PROTO_RSN BIT(1) + +#define WPA_AUTH_ALG_OPEN BIT(0) +#define WPA_AUTH_ALG_SHARED BIT(1) +#define WPA_AUTH_ALG_LEAP BIT(2) + + typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP, - WPA_ALG_IGTK, WPA_ALG_DHV } wpa_alg; + WPA_ALG_IGTK, WPA_ALG_PMK } 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; + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE, + KEY_MGMT_FT_802_1X, KEY_MGMT_FT_PSK, + KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256, + KEY_MGMT_WPS +} wpa_key_mgmt; /** * enum wpa_states - wpa_supplicant state diff --git a/contrib/hostapd/wpa_common.h b/src/common/eapol_common.h index 6d0316ce4a388..d70e62d2f6487 100644 --- a/contrib/hostapd/wpa_common.h +++ b/src/common/eapol_common.h @@ -1,6 +1,6 @@ /* - * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * EAPOL definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,13 +12,8 @@ * See README and COPYING for more details. */ -#ifndef WPA_COMMON_H -#define WPA_COMMON_H - -#define WPA_REPLAY_COUNTER_LEN 8 -#define WPA_NONCE_LEN 32 -#define WPA_KEY_RSC_LEN 8 - +#ifndef EAPOL_COMMON_H +#define EAPOL_COMMON_H /* IEEE Std 802.1X-2004 */ @@ -29,7 +24,7 @@ struct ieee802_1x_hdr { u8 version; u8 type; - u16 length; + be16 length; /* followed by length octets of data */ } STRUCT_PACKED; @@ -49,10 +44,4 @@ enum { IEEE802_1X_TYPE_EAP_PACKET = 0, enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; -#ifdef CONFIG_IEEE80211W -#define WPA_DGTK_LEN 16 -#define WPA_DHV_LEN 16 -#define WPA_IGTK_LEN 16 -#endif /* CONFIG_IEEE80211W */ - -#endif /* WPA_COMMON_H */ +#endif /* EAPOL_COMMON_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c new file mode 100644 index 0000000000000..991c989d0233d --- /dev/null +++ b/src/common/ieee802_11_common.c @@ -0,0 +1,252 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + + +static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, + struct ieee802_11_elems *elems, + int show_errors) +{ + unsigned int oui; + + /* first 3 bytes in vendor specific information element are the IEEE + * OUI of the vendor. The following byte is used a vendor specific + * sub-type. */ + if (elen < 4) { + if (show_errors) { + wpa_printf(MSG_MSGDUMP, "short vendor specific " + "information element ignored (len=%lu)", + (unsigned long) elen); + } + return -1; + } + + oui = WPA_GET_BE24(pos); + switch (oui) { + case OUI_MICROSOFT: + /* Microsoft/Wi-Fi information elements are further typed and + * subtyped */ + switch (pos[3]) { + case 1: + /* Microsoft OUI (00:50:F2) with OUI Type 1: + * real WPA information element */ + elems->wpa_ie = pos; + elems->wpa_ie_len = elen; + break; + case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ + if (elen < 5) { + wpa_printf(MSG_MSGDUMP, "short WME " + "information element ignored " + "(len=%lu)", + (unsigned long) elen); + return -1; + } + switch (pos[4]) { + case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: + case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: + elems->wme = pos; + elems->wme_len = elen; + break; + case WME_OUI_SUBTYPE_TSPEC_ELEMENT: + elems->wme_tspec = pos; + elems->wme_tspec_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "unknown WME " + "information element ignored " + "(subtype=%d len=%lu)", + pos[4], (unsigned long) elen); + return -1; + } + break; + case 4: + /* Wi-Fi Protected Setup (WPS) IE */ + elems->wps_ie = pos; + elems->wps_ie_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown Microsoft " + "information element ignored " + "(type=%d len=%lu)\n", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_BROADCOM: + switch (pos[3]) { + case VENDOR_HT_CAPAB_OUI_TYPE: + elems->vendor_ht_cap = pos; + elems->vendor_ht_cap_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown Broadcom " + "information element ignored " + "(type=%d len=%lu)\n", + pos[3], (unsigned long) elen); + return -1; + } + break; + + default: + wpa_printf(MSG_MSGDUMP, "unknown vendor specific information " + "element ignored (vendor OUI %02x:%02x:%02x " + "len=%lu)", + pos[0], pos[1], pos[2], (unsigned long) elen); + return -1; + } + + return 0; +} + + +/** + * ieee802_11_parse_elems - Parse information elements in management frames + * @start: Pointer to the start of IEs + * @len: Length of IE buffer in octets + * @elems: Data structure for parsed elements + * @show_errors: Whether to show parsing errors in debug log + * Returns: Parsing result + */ +ParseRes ieee802_11_parse_elems(u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors) +{ + size_t left = len; + u8 *pos = start; + int unknown = 0; + + os_memset(elems, 0, sizeof(*elems)); + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) { + if (show_errors) { + wpa_printf(MSG_DEBUG, "IEEE 802.11 element " + "parse failed (id=%d elen=%d " + "left=%lu)", + id, elen, (unsigned long) left); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } + return ParseFailed; + } + + switch (id) { + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_FH_PARAMS: + elems->fh_params = pos; + elems->fh_params_len = elen; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = pos; + elems->ds_params_len = elen; + break; + case WLAN_EID_CF_PARAMS: + elems->cf_params = pos; + elems->cf_params_len = elen; + break; + case WLAN_EID_TIM: + elems->tim = pos; + elems->tim_len = elen; + break; + case WLAN_EID_IBSS_PARAMS: + elems->ibss_params = pos; + elems->ibss_params_len = elen; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_ERP_INFO: + elems->erp_info = pos; + elems->erp_info_len = elen; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = pos; + elems->ext_supp_rates_len = elen; + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (ieee802_11_parse_vendor_specific(pos, elen, + elems, + show_errors)) + unknown++; + break; + case WLAN_EID_RSN: + elems->rsn_ie = pos; + elems->rsn_ie_len = elen; + break; + case WLAN_EID_PWR_CAPABILITY: + elems->power_cap = pos; + elems->power_cap_len = elen; + break; + case WLAN_EID_SUPPORTED_CHANNELS: + elems->supp_channels = pos; + elems->supp_channels_len = elen; + break; + case WLAN_EID_MOBILITY_DOMAIN: + elems->mdie = pos; + elems->mdie_len = elen; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + elems->ftie = pos; + elems->ftie_len = elen; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + elems->timeout_int = pos; + elems->timeout_int_len = elen; + break; + case WLAN_EID_HT_CAP: + elems->ht_capabilities = pos; + elems->ht_capabilities_len = elen; + break; + case WLAN_EID_HT_OPERATION: + elems->ht_operation = pos; + elems->ht_operation_len = elen; + break; + default: + unknown++; + if (!show_errors) + break; + wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " + "ignored unknown element (id=%d elen=%d)", + id, elen); + break; + } + + left -= elen; + pos += elen; + } + + if (left) + return ParseFailed; + + return unknown ? ParseUnknown : ParseOK; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h new file mode 100644 index 0000000000000..2aff9939f4acb --- /dev/null +++ b/src/common/ieee802_11_common.h @@ -0,0 +1,74 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_COMMON_H +#define IEEE802_11_COMMON_H + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ssid; + u8 ssid_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *fh_params; + u8 fh_params_len; + u8 *ds_params; + u8 ds_params_len; + u8 *cf_params; + u8 cf_params_len; + u8 *tim; + u8 tim_len; + u8 *ibss_params; + u8 ibss_params_len; + u8 *challenge; + u8 challenge_len; + u8 *erp_info; + u8 erp_info_len; + u8 *ext_supp_rates; + u8 ext_supp_rates_len; + u8 *wpa_ie; + u8 wpa_ie_len; + u8 *rsn_ie; + u8 rsn_ie_len; + u8 *wme; + u8 wme_len; + u8 *wme_tspec; + u8 wme_tspec_len; + u8 *wps_ie; + u8 wps_ie_len; + u8 *power_cap; + u8 power_cap_len; + u8 *supp_channels; + u8 supp_channels_len; + u8 *mdie; + u8 mdie_len; + u8 *ftie; + u8 ftie_len; + u8 *timeout_int; + u8 timeout_int_len; + u8 *ht_capabilities; + u8 ht_capabilities_len; + u8 *ht_operation; + u8 ht_operation_len; + u8 *vendor_ht_cap; + u8 vendor_ht_cap_len; +}; + +typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; + +ParseRes ieee802_11_parse_elems(u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors); + +#endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h new file mode 100644 index 0000000000000..18220b0fe5c25 --- /dev/null +++ b/src/common/ieee802_11_defs.h @@ -0,0 +1,588 @@ +/* + * IEEE 802.11 Frame type definitions + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_DEFS_H +#define IEEE802_11_DEFS_H + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER 0x0003 +#define WLAN_FC_TODS 0x0100 +#define WLAN_FC_FROMDS 0x0200 +#define WLAN_FC_MOREFRAG 0x0400 +#define WLAN_FC_RETRY 0x0800 +#define WLAN_FC_PWRMGT 0x1000 +#define WLAN_FC_MOREDATA 0x2000 +#define WLAN_FC_ISWEP 0x4000 +#define WLAN_FC_ORDER 0x8000 + +#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) +#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 +#define WLAN_FC_STYPE_ACTION 13 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 +#define WLAN_FC_STYPE_QOS_DATA 8 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) +#define WLAN_CAPABILITY_PBCC BIT(6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) +#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) + +/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* IEEE 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11h */ +#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 +#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 +#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 +/* IEEE 802.11g */ +#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 +#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +/* IEEE 802.11w */ +#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 +#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 +#define WLAN_STATUS_TS_NOT_CREATED 47 +#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 +#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 +#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 +#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 +/* IEEE 802.11r */ +#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 +#define WLAN_STATUS_EXPECTED_RESOURCE_REQ_FT 53 +#define WLAN_STATUS_INVALID_PMKID 54 +#define WLAN_STATUS_INVALID_MDIE 55 +#define WLAN_STATUS_INVALID_FTIE 56 + +/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11h */ +#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 +#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_COUNTRY 7 +#define WLAN_EID_CHALLENGE 16 +/* EIDs defined by IEEE 802.11h - START */ +#define WLAN_EID_PWR_CONSTRAINT 32 +#define WLAN_EID_PWR_CAPABILITY 33 +#define WLAN_EID_TPC_REQUEST 34 +#define WLAN_EID_TPC_REPORT 35 +#define WLAN_EID_SUPPORTED_CHANNELS 36 +#define WLAN_EID_CHANNEL_SWITCH 37 +#define WLAN_EID_MEASURE_REQUEST 38 +#define WLAN_EID_MEASURE_REPORT 39 +#define WLAN_EID_QUITE 40 +#define WLAN_EID_IBSS_DFS 41 +/* EIDs defined by IEEE 802.11h - END */ +#define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_HT_CAP 45 +#define WLAN_EID_RSN 48 +#define WLAN_EID_EXT_SUPP_RATES 50 +#define WLAN_EID_MOBILITY_DOMAIN 54 +#define WLAN_EID_FAST_BSS_TRANSITION 55 +#define WLAN_EID_TIMEOUT_INTERVAL 56 +#define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_HT_OPERATION 61 +#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_20_40_BSS_COEXISTENCE 72 +#define WLAN_EID_20_40_BSS_INTOLERANT 73 +#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 +#define WLAN_EID_MMIE 76 +#define WLAN_EID_VENDOR_SPECIFIC 221 + + +/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ +#define WLAN_ACTION_SPECTRUM_MGMT 0 +#define WLAN_ACTION_QOS 1 +#define WLAN_ACTION_DLS 2 +#define WLAN_ACTION_BLOCK_ACK 3 +#define WLAN_ACTION_RADIO_MEASUREMENT 5 +#define WLAN_ACTION_FT 6 +#define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_WMM 17 + +/* SA Query Action frame (IEEE 802.11w/D7.0, 7.4.9) */ +#define WLAN_SA_QUERY_REQUEST 0 +#define WLAN_SA_QUERY_RESPONSE 1 + +#define WLAN_SA_QUERY_TR_ID_LEN 16 + +/* Timeout Interval Type */ +#define WLAN_TIMEOUT_REASSOC_DEADLINE 1 +#define WLAN_TIMEOUT_KEY_LIFETIME 2 +#define WLAN_TIMEOUT_ASSOC_COMEBACK 3 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee80211_mgmt { + le16 frame_control; + le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + le16 seq_ctrl; + union { + struct { + le16 auth_alg; + le16 auth_transaction; + le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } STRUCT_PACKED auth; + struct { + le16 reason_code; + } STRUCT_PACKED deauth; + struct { + le16 capab_info; + le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_req; + struct { + le16 capab_info; + le16 status_code; + le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_resp, reassoc_resp; + struct { + le16 capab_info; + le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED reassoc_req; + struct { + le16 reason_code; + } STRUCT_PACKED disassoc; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } STRUCT_PACKED beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } STRUCT_PACKED probe_req; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } STRUCT_PACKED probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } STRUCT_PACKED wme_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + u8 switch_mode; + u8 new_chan; + u8 switch_count; + } STRUCT_PACKED chan_switch; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_req; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + le16 status_code; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_resp; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_req; + struct { + u8 action; /* */ + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_resp; + } u; + } STRUCT_PACKED action; + } u; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define ERP_INFO_NON_ERP_PRESENT BIT(0) +#define ERP_INFO_USE_PROTECTION BIT(1) +#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) + + +/* HT Capability element */ + +enum { + MAX_RX_AMPDU_FACTOR_8KB = 0, + MAX_RX_AMPDU_FACTOR_16KB, + MAX_RX_AMPDU_FACTOR_32KB, + MAX_RX_AMPDU_FACTOR_64KB +}; + +enum { + CALIBRATION_NOT_SUPPORTED = 0, + CALIBRATION_CANNOT_INIT, + CALIBRATION_CAN_INIT, + CALIBRATION_FULL_SUPPORT +}; + +enum { + MCS_FEEDBACK_NOT_PROVIDED = 0, + MCS_FEEDBACK_UNSOLICITED, + MCS_FEEDBACK_MRQ_RESPONSE +}; + + +struct ieee80211_ht_capability { + le16 capabilities_info; + u8 mac_ht_params_info; + u8 supported_mcs_set[16]; + le16 extended_ht_capability_info; + le32 tx_BF_capability_info; + u8 antenna_selection_info; +} STRUCT_PACKED; + + +struct ieee80211_ht_operation { + u8 control_chan; + u8 ht_param; + le16 operation_mode; + le16 stbc_param; + u8 basic_set[16]; +} STRUCT_PACKED; + +/* auxiliary bit manipulation macros FIXME: move it to common later... */ +#define SET_2BIT_U8(_ptr_, _shift_, _val_) \ + ((*(_ptr_) &= ~(3 << (_shift_))), \ + (*(_ptr_) |= (*(_ptr_) & (((u8)3) << (_shift_))) | \ + (((u8)(_val_) & 3) << _shift_))) + +#define GET_2BIT_U8(_var_, _shift_) \ + (((_var_) & (((u8)3) << (_shift_))) >> (_shift_)) + +#define SET_2BIT_LE16(_u16ptr_, _shift_, _val_) \ + ((*(_u16ptr_) &= ~(3 << (_shift_))), \ + (*(_u16ptr_) |= \ + (((*(_u16ptr_)) & (((u16)3) << ((u16)_shift_))) | \ + (((u16)(_val_) & (u16)3) << (u16)(_shift_))))) + +#define GET_2BIT_LE16(_var_, _shift_) \ + (((_var_) & (((u16)3) << (_shift_))) >> (_shift_)) + +#define SET_2BIT_LE32(_u32ptr_, _shift_, _val_) \ + ((*(_u32ptr_) &= ~(3 << (_shift_))), \ + (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)3) << (_shift_))) | \ + (((u32)(_val_) & 3) << _shift_)))) + +#define GET_2BIT_LE32(_var_, _shift_) \ + (((_var_) & (((u32)3) << (_shift_))) >> (_shift_)) + +#define SET_3BIT_LE16(_u16ptr_, _shift_, _val_) \ + ((*(_u16ptr_) &= ~(7 << (_shift_))), \ + (*(_u16ptr_) |= (((*(_u16ptr_)) & (((u16)7) << (_shift_))) | \ + (((u16)(_val_) & 7) << _shift_)))) + +#define GET_3BIT_LE16(_var_, _shift_) \ + (((_var_) & (((u16)7) << (_shift_))) >> (_shift_)) + +#define SET_3BIT_LE32(_u32ptr_, _shift_, _val_) \ + ((*(_u32ptr_) &= ~(7 << (_shift_))), \ + (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)7) << (_shift_))) | \ + (((u32)(_val_) & 7) << _shift_)))) + +#define GET_3BIT_LE32(_var_, _shift_) \ + (((_var_) & (((u32)7) << (_shift_))) >> (_shift_)) + + +#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) +#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) +#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_SMPS_STATIC ((u16) 0) +#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2)) +#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) +#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) +#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) +#define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) +#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8)) +#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9)) +#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) +#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) +#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) +#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) +#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) +#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) + + +#define MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET 0 +#define MAC_HT_PARAM_INFO_MAX_MPDU_DENSITY_OFFSET 2 + +#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) +#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 +#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) + + +#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) +#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) +#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) +#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) +#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) +#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) +#define TX_BEAMFORM_CAP_CALIB_OFFSET 6 +#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 +#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 +#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 +#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 +#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 +#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 +#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 + + +#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) +#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) +#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) + + +struct ht_cap_ie { + u8 id; + u8 length; + struct ieee80211_ht_capability data; +} STRUCT_PACKED; + + +#define REC_TRANS_CHNL_WIDTH_20 0 +#define REC_TRANS_CHNL_WIDTH_ANY 1 + +#define OP_MODE_PURE 0 +#define OP_MODE_MAY_BE_LEGACY_STAS 1 +#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 +#define OP_MODE_MIXED 3 + +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) +#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) +#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) + +#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ + ((le16) (0x0001 | 0x0002)) +#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 +#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) +#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) +#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) + +#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) +#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) +#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) +#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) +#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) +#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) + + +/* Secondary channel offset element */ +#define SECONDARY_CHANNEL_OFFSET_NONE 0 +#define SECONDARY_CHANNEL_OFFSET_ABOVE 1 +#define SECONDARY_CHANNEL_OFFSET_BELOW 3 +struct secondary_channel_offset_ie { + u8 id; + u8 length; + u8 secondary_offset_offset; +} STRUCT_PACKED; + + +/* body of Recommended Transmit Channel Width action frame */ +#define CHANNEL_WIDTH_20 0 +#define CHANNEL_WIDTH_ANY 1 +struct recommended_tx_channel_width_action { + u8 category; + u8 action; + u8 channel_width; +} STRUCT_PACKED; + +/* body of MIMO Power Save action frame */ +#define PWR_SAVE_MODE_STATIC 0 +#define PWR_SAVE_MODE_DYNAMIC 1 +struct mimo_pwr_save_action { + u8 category; + u8 action; + u8 enable; + u8 mode; +} STRUCT_PACKED; + + +#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) + * 00:50:F2 */ + +#define WME_OUI_TYPE 2 +#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0 +#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1 +#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2 +#define WME_VERSION 1 + +#define WME_ACTION_CODE_SETUP_REQUEST 0 +#define WME_ACTION_CODE_SETUP_RESPONSE 1 +#define WME_ACTION_CODE_TEARDOWN 2 + +#define WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED 0 +#define WME_SETUP_RESPONSE_STATUS_INVALID_PARAMETERS 1 +#define WME_SETUP_RESPONSE_STATUS_REFUSED 3 + +#define WME_TSPEC_DIRECTION_UPLINK 0 +#define WME_TSPEC_DIRECTION_DOWNLINK 1 +#define WME_TSPEC_DIRECTION_BI_DIRECTIONAL 3 + + +#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ + +#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ + +#endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/nl80211_copy.h b/src/common/nl80211_copy.h new file mode 100644 index 0000000000000..8802d1bda382a --- /dev/null +++ b/src/common/nl80211_copy.h @@ -0,0 +1,909 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006, 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2008 Michael Wu <flamingice@sourmilk.net> + * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> + * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008 Luis R. Rodriguez <lrodriguez@atheros.com> + * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> + * Copyright 2008 Colin McCabe <colin@cozybit.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC and %NL80211_ATTR_KEY_CIPHER + * attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + + NL80211_CMD_SET_MGMT_EXTRA_IE, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211/<phyname>/index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags. + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the + * scan result list changes (BSS expired or added) so that applications + * can verify that they got a single, consistent snapshot (when all dump + * messages carried the same generation number) + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_PARAMS, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_SCAN_GENERATION, + NL80211_ATTR_BSS, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST, + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_sta_info_txrate. + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_DSN_VALID: the mesh path contains a valid DSN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_DSN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination + * @NL80211_ATTR_MPATH_DSN: destination sequence number + * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path + * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now + * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_DSN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin) + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h new file mode 100644 index 0000000000000..81b7f5432eccd --- /dev/null +++ b/src/common/privsep_commands.h @@ -0,0 +1,78 @@ +/* + * WPA Supplicant - privilege separation commands + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PRIVSEP_COMMANDS_H +#define PRIVSEP_COMMANDS_H + +enum privsep_cmd { + PRIVSEP_CMD_REGISTER, + PRIVSEP_CMD_UNREGISTER, + PRIVSEP_CMD_SET_WPA, + PRIVSEP_CMD_SCAN, + PRIVSEP_CMD_GET_SCAN_RESULTS, + PRIVSEP_CMD_ASSOCIATE, + PRIVSEP_CMD_GET_BSSID, + PRIVSEP_CMD_GET_SSID, + PRIVSEP_CMD_SET_KEY, + PRIVSEP_CMD_GET_CAPA, + PRIVSEP_CMD_L2_REGISTER, + PRIVSEP_CMD_L2_UNREGISTER, + PRIVSEP_CMD_L2_NOTIFY_AUTH_START, + PRIVSEP_CMD_L2_SEND, + PRIVSEP_CMD_SET_MODE, + PRIVSEP_CMD_SET_COUNTRY, +}; + +struct privsep_cmd_associate +{ + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int freq; + int pairwise_suite; + int group_suite; + int key_mgmt_suite; + int auth_alg; + int mode; + size_t wpa_ie_len; + /* followed by wpa_ie_len bytes of wpa_ie */ +}; + +struct privsep_cmd_set_key +{ + int alg; + u8 addr[ETH_ALEN]; + int key_idx; + int set_tx; + u8 seq[8]; + size_t seq_len; + u8 key[32]; + size_t key_len; +}; + +enum privsep_event { + PRIVSEP_EVENT_SCAN_RESULTS, + PRIVSEP_EVENT_ASSOC, + PRIVSEP_EVENT_DISASSOC, + PRIVSEP_EVENT_ASSOCINFO, + PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, + PRIVSEP_EVENT_INTERFACE_STATUS, + PRIVSEP_EVENT_PMKID_CANDIDATE, + PRIVSEP_EVENT_STKSTART, + PRIVSEP_EVENT_FT_RESPONSE, + PRIVSEP_EVENT_RX_EAPOL, + PRIVSEP_EVENT_STA_RX, +}; + +#endif /* PRIVSEP_COMMANDS_H */ diff --git a/contrib/hostapd/version.h b/src/common/version.h index 364d8aea9657f..9c4c6ca459b34 100644 --- a/contrib/hostapd/version.h +++ b/src/common/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.5.10" +#define VERSION_STR "0.6.8" #endif /* VERSION_H */ diff --git a/src/common/wireless_copy.h b/src/common/wireless_copy.h new file mode 100644 index 0000000000000..ad764663766f8 --- /dev/null +++ b/src/common/wireless_copy.h @@ -0,0 +1,1099 @@ +/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18. + * I have just removed kernel related headers and added some typedefs etc. to + * make this easier to include into user space programs. + * Jouni Malinen, 2005-03-12. + */ + + +/* + * This file define a set of standard wireless extensions + * + * Version : 19 18.3.05 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _LINUX_WIRELESS_H +#define _LINUX_WIRELESS_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial APIs (1996 -> onward) : + * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * + * The entry /proc/net/wireless give statistics and information on the + * driver. + * This is better than having each driver having its entry because + * its centralised and we may remove the driver module safely. + * + * Ioctl are used to configure the driver and issue commands. This is + * better than command line options of insmod because we may want to + * change dynamically (while the driver is running) some parameters. + * + * The ioctl mechanimsm are copied from standard devices ioctl. + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h + * + * Note as well that /proc/net/wireless implementation has now moved in : + * # net/core/wireless.c + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented in : + * # net/core/wireless.c + * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. + * + * These wireless extensions are not magic : each driver has to provide + * support for them... + * + * IMPORTANT NOTE : As everything in the kernel, this is very much a + * work in progress. Contact me if you have ideas of improvements... + */ + +/***************************** INCLUDES *****************************/ + + /* jkm - replaced linux headers with C library headers, added typedefs */ +#if 0 +/* To minimise problems in user space, I might remove those headers + * at some point. Jean II */ +#include <linux/types.h> /* for "caddr_t" et al */ +#include <linux/socket.h> /* for "struct sockaddr" et al */ +#include <linux/if.h> /* for IFNAMSIZ and co... */ +#else +#include <sys/types.h> +#include <net/if.h> +typedef __uint32_t __u32; +typedef __int32_t __s32; +typedef __uint16_t __u16; +typedef __int16_t __s16; +typedef __uint8_t __u8; +#ifndef __user +#define __user +#endif /* __user */ +#endif + +/***************************** VERSION *****************************/ +/* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +#define WIRELESS_EXT 19 + +/* + * Changes : + * + * V2 to V3 + * -------- + * Alan Cox start some incompatibles changes. I've integrated a bit more. + * - Encryption renamed to Encode to avoid US regulation problems + * - Frequency changed from float to struct to avoid problems on old 386 + * + * V3 to V4 + * -------- + * - Add sensitivity + * + * V4 to V5 + * -------- + * - Missing encoding definitions in range + * - Access points stuff + * + * V5 to V6 + * -------- + * - 802.11 support (ESSID ioctls) + * + * V6 to V7 + * -------- + * - define IW_ESSID_MAX_SIZE and IW_MAX_AP + * + * V7 to V8 + * -------- + * - Changed my e-mail address + * - More 802.11 support (nickname, rate, rts, frag) + * - List index in frequencies + * + * V8 to V9 + * -------- + * - Support for 'mode of operation' (ad-hoc, managed...) + * - Support for unicast and multicast power saving + * - Change encoding to support larger tokens (>64 bits) + * - Updated iw_params (disable, flags) and use it for NWID + * - Extracted iw_point from iwreq for clarity + * + * V9 to V10 + * --------- + * - Add PM capability to range structure + * - Add PM modifier : MAX/MIN/RELATIVE + * - Add encoding option : IW_ENCODE_NOKEY + * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor + * + * V15 to V16 + * ---------- + * - Increase the number of bitrates in iw_range to 32 (for 802.11g) + * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) + * - Reshuffle struct iw_range for increases, add filler + * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses + * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support + * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" + * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen <j@w1.fi>) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND + * + * V18 to V19 + * ---------- + * - Remove (struct iw_point *)->pointer from events and streams + * - Remove header includes to help user space + * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 + * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros + * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM + * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + */ + +/**************************** CONSTANTS ****************************/ + +/* -------------------------- IOCTL LIST -------------------------- */ + +/* Wireless Identification */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ +#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ +#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ +#define SIOCSIWMODE 0x8B06 /* set operation mode */ +#define SIOCGIWMODE 0x8B07 /* get operation mode */ +#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ +#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ + +/* Informative stuff */ +#define SIOCSIWRANGE 0x8B0A /* Unused */ +#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ +#define SIOCSIWPRIV 0x8B0C /* Unused */ +#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ + +/* Spy support (statistics per MAC address - used for Mobile IP support) */ +#define SIOCSIWSPY 0x8B10 /* set spy addresses */ +#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ +#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ +#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ + +/* Access Point manipulation */ +#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ +#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + +/* 802.11 specific support */ +#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +#define SIOCGIWESSID 0x8B1B /* get ESSID */ +#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ +#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ +/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to + * point to a string in user space, like it is done for RANGE... */ + +/* Other parameters useful in 802.11 and some other devices */ +#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ +#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ +#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ +#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ +#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ +#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ +#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ + +/* Encoding stuff (scrambling, hardware security, WEP...) */ +#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ +/* Power saving stuff (power management, unicast and multicast) */ +#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ +#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ + +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 32 ioctl are wireless device private, for 16 commands. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'odd' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you + * must be compliant with it. + */ + +/* ------------------------- IOCTL STUFF ------------------------- */ + +/* The first and the last (range) */ +#define SIOCIWFIRST 0x8B00 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) + +/* Even : get (world access), odd : set (root access) */ +#define IW_IS_SET(cmd) (!((cmd) & 0x1)) +#define IW_IS_GET(cmd) ((cmd) & 0x1) + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + +/* ------------------------- PRIVATE INFO ------------------------- */ +/* + * The following is used with SIOCGIWPRIV. It allow a driver to define + * the interface (name, type of data) for its private ioctl. + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV + */ + +#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ +#define IW_PRIV_TYPE_NONE 0x0000 +#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ +#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ +#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ + +#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ + +/* + * Note : if the number of args is fixed and the size < 16 octets, + * instead of passing a pointer we will put args in the iwreq struct... + */ + +/* ----------------------- OTHER CONSTANTS ----------------------- */ + +/* Maximum frequencies in the range struct */ +#define IW_MAX_FREQUENCIES 32 +/* Note : if you have something like 80 frequencies, + * don't increase this constant and don't fill the frequency list. + * The user will be able to set by channel anyway... */ + +/* Maximum bit rates in the range struct */ +#define IW_MAX_BITRATES 32 + +/* Maximum tx powers in the range struct */ +#define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ + +/* Maximum of address that you may set with SPY */ +#define IW_MAX_SPY 8 + +/* Maximum of address that you may get in the + list of access points in range */ +#define IW_MAX_AP 64 + +/* Maximum size of the ESSID and NICKN strings */ +#define IW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define IW_MODE_AUTO 0 /* Let the driver decides */ +#define IW_MODE_ADHOC 1 /* Single cell network */ +#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ + +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_ALL_INVALID 0x70 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + +/* Maximum number of size of encoding token available + * they are listed in the range structure */ +#define IW_MAX_ENCODING_SIZES 8 + +/* Maximum size of the encoding token in bytes */ +#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ + +/* Flags for encoding (along with the token) */ +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + +/* Power management flags available (along with the value, if any) */ +#define IW_POWER_ON 0x0000 /* No details... */ +#define IW_POWER_TYPE 0xF000 /* Type of parameter */ +#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ +#define IW_POWER_MODE 0x0F00 /* Power Management mode */ +#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ +#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ +#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ +#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ +#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ +#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ +#define IW_POWER_MIN 0x0001 /* Value is a minimum */ +#define IW_POWER_MAX 0x0002 /* Value is a maximum */ +#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ +#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ +#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* IW_AUTH_MFP (management frame protection) values */ +#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ +#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ +#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + +/****************************** TYPES ******************************/ + +/* --------------------------- SUBTYPES --------------------------- */ +/* + * Generic format for most parameters that fit in an int + */ +struct iw_param +{ + __s32 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct iw_point +{ + void __user *pointer; /* Pointer to the data (in user space) */ + __u16 length; /* number of fields or size in bytes */ + __u16 flags; /* Optional params */ +}; + +/* + * A frequency + * For numbers lower than 10^9, we encode the number in 'm' and + * set 'e' to 0 + * For number greater than 10^9, we divide it by the lowest power + * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... + * The power of 10 is in 'e', the result of the division is in 'm'. + */ +struct iw_freq +{ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ + __u8 flags; /* Flags (fixed/auto) */ +}; + +/* + * Quality of the link + */ +struct iw_quality +{ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ + __u8 updated; /* Flags to know if updated */ +}; + +/* + * Packet discarded in the wireless adapter due to + * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... + */ +struct iw_discarded +{ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ + __u32 misc; /* Others cases */ +}; + +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + +/* + * Quality range (for spy threshold) + */ +struct iw_thrspy +{ + struct sockaddr addr; /* Source address (hw/mac) */ + struct iw_quality qual; /* Quality of the link */ + struct iw_quality low; /* Low threshold */ + struct iw_quality high; /* High threshold */ +}; + +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; + __u8 key[0]; +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr bssid; +}; + +/* ------------------------ WIRELESS STATS ------------------------ */ +/* + * Wireless statistics (used for /proc/net/wireless) + */ +struct iw_statistics +{ + __u16 status; /* Status + * - device dependent for now */ + + struct iw_quality qual; /* Quality of the link + * (instant/mean/max) */ + struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ +}; + +/* ------------------------ IOCTL REQUEST ------------------------ */ +/* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ + struct sockaddr addr; /* Destination address (hw/mac) */ + + struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ +}; + +/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... + * Do I need to remind you about structure size (32 octets) ? + */ +struct iwreq +{ + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part (defined just above) */ + union iwreq_data u; +}; + +/* -------------------------- IOCTL DATA -------------------------- */ +/* + * For those ioctl which want to exchange mode data that what could + * fit in the above structure... + */ + +/* + * Range of parameters + */ + +struct iw_range +{ + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; + + /* signal level threshold range */ + __s32 sensitivity; + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... */ + struct iw_quality max_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + struct iw_quality avg_qual; /* Quality of the link */ + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ + __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ + __s32 max_rts; /* Maximal RTS threshold */ + + /* Frag threshold */ + __s32 min_frag; /* Minimal frag threshold */ + __s32 max_frag; /* Maximal frag threshold */ + + /* Power Management duration & timeout */ + __s32 min_pmp; /* Minimal PM period */ + __s32 max_pmp; /* Maximal PM period */ + __s32 min_pmt; /* Minimal PM timeout */ + __s32 max_pmt; /* Maximal PM timeout */ + __u16 pmp_flags; /* How to decode max/min PM period */ + __u16 pmt_flags; /* How to decode max/min PM timeout */ + __u16 pm_capa; /* What PM options are supported */ + + /* Encoder stuff */ + __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ + __u8 num_encoding_sizes; /* Number of entry in the list */ + __u8 max_encoding_tokens; /* Max number of tokens */ + /* For drivers that need a "login/passwd" form */ + __u8 encoding_login_index; /* token index for login token */ + + /* Transmit power */ + __u16 txpower_capa; /* What options are supported */ + __u8 num_txpower; /* Number of entries in the list */ + __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Frequency */ + __u16 num_channels; /* Number of channels [0; num - 1] */ + __u8 num_frequency; /* Number of entry in the list */ + struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ + /* Note : this frequency list doesn't need to fit channel numbers, + * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ +}; + +/* + * Private ioctl interface information + */ + +struct iw_priv_args +{ + __u32 cmd; /* Number of the ioctl to issue */ + __u16 set_args; /* Type and number of args */ + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real lenght of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* iw_point events are special. First, the payload (extra data) come at + * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, + * we omit the pointer, so start at an offset. */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ + IW_EV_POINT_OFF) + +#endif /* _LINUX_WIRELESS_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c new file mode 100644 index 0000000000000..c9ff319ccf6ae --- /dev/null +++ b/src/common/wpa_common.c @@ -0,0 +1,570 @@ +/* + * WPA/RSN - Shared functions for supplicant and authenticator + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "ieee802_11_defs.h" +#include "defs.h" +#include "wpa_common.h" + + +/** + * wpa_eapol_key_mic - Calculate EAPOL-Key MIC + * @key: EAPOL-Key Key Confirmation Key (KCK) + * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) + * @buf: Pointer to the beginning of the EAPOL header (version field) + * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) + * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: 0 on success, -1 on failure + * + * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has + * to be cleared (all zeroes) when calling this function. + * + * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the + * description of the Key MIC calculation. It includes packet data from the + * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change + * happened during final editing of the standard and the correct behavior is + * defined in the last draft (IEEE 802.11i/D10). + */ +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic) +{ + u8 hash[SHA1_MAC_LEN]; + + switch (ver) { + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + hmac_md5(key, 16, buf, len, mic); + break; + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + hmac_sha1(key, 16, buf, len, hash); + os_memcpy(mic, hash, MD5_MAC_LEN); + break; +#ifdef CONFIG_IEEE80211R + case WPA_KEY_INFO_TYPE_AES_128_CMAC: + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_IEEE80211R */ + default: + return -1; + } + + return 0; +} + + +/** + * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces + * @pmk: Pairwise master key + * @pmk_len: Length of PMK + * @label: Label to use in derivation + * @addr1: AA or SA + * @addr2: SA or AA + * @nonce1: ANonce or SNonce + * @nonce2: SNonce or ANonce + * @ptk: Buffer for pairwise transient key + * @ptk_len: Length of PTK + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * + * STK = PRF-X(SMK, "Peer key expansion", + * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || + * Min(INonce, PNonce) || Max(INonce, PNonce)) + */ +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { + os_memcpy(data, addr1, ETH_ALEN); + os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(data, addr2, ETH_ALEN); + os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { + os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); + os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, + WPA_NONCE_LEN); + } else { + os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); + os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, + WPA_NONCE_LEN); + } + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + sha256_prf(pmk, pmk_len, label, data, sizeof(data), + ptk, ptk_len); + else +#endif /* CONFIG_IEEE80211W */ + sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk, + ptk_len); + + wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, + MAC2STR(addr1), MAC2STR(addr2)); + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); +} + + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, + u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, + const u8 *ftie, size_t ftie_len, + const u8 *rsnie, size_t rsnie_len, + const u8 *ric, size_t ric_len, u8 *mic) +{ + u8 *buf, *pos; + size_t buf_len; + + buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + pos = buf; + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ap_addr, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = transaction_seqnum; + if (rsnie) { + os_memcpy(pos, rsnie, rsnie_len); + pos += rsnie_len; + } + if (mdie) { + os_memcpy(pos, mdie, mdie_len); + pos += mdie_len; + } + if (ftie) { + struct rsn_ftie *_ftie; + os_memcpy(pos, ftie, ftie_len); + if (ftie_len < 2 + sizeof(*_ftie)) { + os_free(buf); + return -1; + } + _ftie = (struct rsn_ftie *) (pos + 2); + os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); + pos += ftie_len; + } + if (ric) { + os_memcpy(pos, ric, ric_len); + pos += ric_len; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); + if (omac1_aes_128(kck, buf, pos - buf, mic)) { + os_free(buf); + return -1; + } + + os_free(buf); + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +#ifndef CONFIG_NO_WPA2 +static int rsn_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; +#ifdef CONFIG_IEEE80211R + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) + return WPA_KEY_MGMT_FT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) + return WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) + return WPA_KEY_MGMT_IEEE8021X_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) + return WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + return 0; +} +#endif /* CONFIG_NO_WPA2 */ + + +/** + * wpa_parse_wpa_ie_rsn - Parse RSN IE + * @rsn_ie: Buffer containing RSN IE + * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) + * @data: Pointer to structure that will be filled in with parsed data + * Returns: 0 on success, <0 on failure + */ +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ +#ifndef CONFIG_NO_WPA2 + const struct rsn_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_RSN; + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; +#ifdef CONFIG_IEEE80211W + data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; +#else /* CONFIG_IEEE80211W */ + data->mgmt_group_cipher = 0; +#endif /* CONFIG_IEEE80211W */ + + if (rsn_ie_len == 0) { + /* No RSN IE - fail silently */ + return -1; + } + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); +#ifdef CONFIG_IEEE80211W + if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group " + "cipher", __func__); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#ifdef CONFIG_IEEE80211W + if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as " + "pairwise cipher", __func__); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (left < (int) data->num_pmkid * PMKID_LEN) { + wpa_printf(MSG_DEBUG, "%s: PMKID underflow " + "(num_pmkid=%lu left=%d)", + __func__, (unsigned long) data->num_pmkid, + left); + data->num_pmkid = 0; + return -9; + } else { + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + } + +#ifdef CONFIG_IEEE80211W + if (left >= 4) { + data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); + if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: Unsupported management " + "group cipher 0x%x", __func__, + data->mgmt_group_cipher); + return -10; + } + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + + +#ifdef CONFIG_IEEE80211R + +/** + * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name + * + * IEEE Std 802.11r-2008 - 8.5.1.5.3 + */ +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) +{ + u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; + u8 *pos, r0_key_data[48], hash[32]; + const u8 *addr[2]; + size_t len[2]; + + /* + * R0-Key-Data = KDF-384(XXKey, "FT-R0", + * SSIDlength || SSID || MDID || R0KHlength || + * R0KH-ID || S0KH-ID) + * XXKey is either the second 256 bits of MSK or PSK. + * PMK-R0 = L(R0-Key-Data, 0, 256) + * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) + */ + if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) + return; + pos = buf; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + pos += ssid_len; + os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + os_memcpy(pos, s0kh_id, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, sizeof(r0_key_data)); + os_memcpy(pmk_r0, r0_key_data, PMK_LEN); + + /* + * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) + */ + addr[0] = (const u8 *) "FT-R0N"; + len[0] = 6; + addr[1] = r0_key_data + PMK_LEN; + len[1] = 16; + + sha256_vector(2, addr, len, hash); + os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); +} + + +/** + * wpa_derive_pmk_r1_name - Derive PMKR1Name + * + * IEEE Std 802.11r-2008 - 8.5.1.5.4 + */ +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name) +{ + u8 hash[32]; + const u8 *addr[4]; + size_t len[4]; + + /* + * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || + * R1KH-ID || S1KH-ID)) + */ + addr[0] = (const u8 *) "FT-R1N"; + len[0] = 6; + addr[1] = pmk_r0_name; + len[1] = WPA_PMK_NAME_LEN; + addr[2] = r1kh_id; + len[2] = FT_R1KH_ID_LEN; + addr[3] = s1kh_id; + len[3] = ETH_ALEN; + + sha256_vector(4, addr, len, hash); + os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); +} + + +/** + * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0 + * + * IEEE Std 802.11r-2008 - 8.5.1.5.4 + */ +void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name) +{ + u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; + u8 *pos; + + /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ + pos = buf; + os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + os_memcpy(pos, s1kh_id, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN); + + wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name); +} + + +/** + * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1 + * + * IEEE Std 802.11r-2008 - 8.5.1.5.5 + */ +void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, + const u8 *sta_addr, const u8 *bssid, + const u8 *pmk_r1_name, + u8 *ptk, size_t ptk_len, u8 *ptk_name) +{ + u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; + u8 *pos, hash[32]; + const u8 *addr[6]; + size_t len[6]; + + /* + * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || + * BSSID || STA-ADDR) + */ + pos = buf; + os_memcpy(pos, snonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, anonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, bssid, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len); + + /* + * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || + * ANonce || BSSID || STA-ADDR)) + */ + addr[0] = pmk_r1_name; + len[0] = WPA_PMK_NAME_LEN; + addr[1] = (const u8 *) "FT-PTKN"; + len[1] = 7; + addr[2] = snonce; + len[2] = WPA_NONCE_LEN; + addr[3] = anonce; + len[3] = WPA_NONCE_LEN; + addr[4] = bssid; + len[4] = ETH_ALEN; + addr[5] = sta_addr; + len[5] = ETH_ALEN; + + sha256_vector(6, addr, len, hash); + os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h new file mode 100644 index 0000000000000..3074cd44a1365 --- /dev/null +++ b/src/common/wpa_common.h @@ -0,0 +1,335 @@ +/* + * WPA definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_COMMON_H +#define WPA_COMMON_H + +#define WPA_MAX_SSID_LEN 32 + +/* IEEE 802.11i */ +#define PMKID_LEN 16 +#define PMK_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 +#define WPA_KEY_RSC_LEN 8 +#define WPA_GMK_LEN 32 +#define WPA_GTK_MAX_LEN 32 + +#define WPA_SELECTOR_LEN 4 +#define WPA_VERSION 1 +#define RSN_SELECTOR_LEN 4 +#define RSN_VERSION 1 + +#define RSN_SELECTOR(a, b, c, d) \ + ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ + (u32) (d)) + +#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#if 0 +#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) +#endif +#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) +#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) + + +#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#ifdef CONFIG_IEEE80211R +#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#endif /* CONFIG_IEEE80211R */ +#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) + +#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) +#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#if 0 +#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#endif +#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#ifdef CONFIG_IEEE80211W +#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#endif /* CONFIG_IEEE80211W */ + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. + */ +#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#if 0 +#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#endif +#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#ifdef CONFIG_PEERKEY +#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#endif /* CONFIG_IEEE80211W */ + +#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) + +#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) +#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a)) + +#define RSN_NUM_REPLAY_COUNTERS_1 0 +#define RSN_NUM_REPLAY_COUNTERS_2 1 +#define RSN_NUM_REPLAY_COUNTERS_4 2 +#define RSN_NUM_REPLAY_COUNTERS_16 3 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#ifdef CONFIG_IEEE80211W +#define WPA_IGTK_LEN 16 +#endif /* CONFIG_IEEE80211W */ + + +/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */ +#define WPA_CAPABILITY_PREAUTH BIT(0) +#define WPA_CAPABILITY_NO_PAIRWISE BIT(1) +/* B2-B3: PTKSA Replay Counter */ +/* B4-B5: GTKSA Replay Counter */ +#define WPA_CAPABILITY_MFPR BIT(6) +#define WPA_CAPABILITY_MFPC BIT(7) +#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) + + +/* IEEE 802.11r */ +#define MOBILITY_DOMAIN_ID_LEN 2 +#define FT_R0KH_ID_MAX_LEN 48 +#define FT_R1KH_ID_LEN 6 +#define WPA_PMK_NAME_LEN 16 + + +/* IEEE 802.11, 8.5.2 EAPOL-Key frames */ +#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2))) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3 +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ +#define WPA_KEY_INFO_SMK_MESSAGE BIT(13) + + +struct wpa_eapol_key { + u8 type; + /* Note: key_info, key_length, and key_data_length are unaligned */ + u8 key_info[2]; /* big endian */ + u8 key_length[2]; /* big endian */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[WPA_KEY_RSC_LEN]; + u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ + u8 key_mic[16]; + u8 key_data_length[2]; /* big endian */ + /* followed by key_data_length bytes of key_data */ +} STRUCT_PACKED; + +/** + * struct wpa_ptk - WPA Pairwise Transient Key + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + */ +struct wpa_ptk { + u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ + u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} STRUCT_PACKED; + + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */ + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +/* 1/4: PMKID + * 2/4: RSN IE + * 3/4: one or two RSN IEs + GTK IE (encrypted) + * 4/4: empty + * 1/2: GTK IE (encrypted) + * 2/2: empty + */ + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +#ifdef CONFIG_PEERKEY +enum { + STK_MUI_4WAY_STA_AP = 1, + STK_MUI_4WAY_STAT_STA = 2, + STK_MUI_GTK = 3, + STK_MUI_SMK = 4 +}; + +enum { + STK_ERR_STA_NR = 1, + STK_ERR_STA_NRSN = 2, + STK_ERR_CPHR_NS = 3, + STK_ERR_NO_STSL = 4 +}; +#endif /* CONFIG_PEERKEY */ + +struct rsn_error_kde { + be16 mui; + be16 error_type; +} STRUCT_PACKED; + +#ifdef CONFIG_IEEE80211W +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R +struct rsn_mdie { + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 ft_capab; +} STRUCT_PACKED; + +#define RSN_FT_CAPAB_FT_OVER_DS BIT(0) +#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1) + +struct rsn_ftie { + u8 mic_control[2]; + u8 mic[16]; + u8 anonce[WPA_NONCE_LEN]; + u8 snonce[WPA_NONCE_LEN]; + /* followed by optional parameters */ +} STRUCT_PACKED; + +#define FTIE_SUBELEM_R1KH_ID 1 +#define FTIE_SUBELEM_GTK 2 +#define FTIE_SUBELEM_R0KH_ID 3 +#define FTIE_SUBELEM_IGTK 4 + +#endif /* CONFIG_IEEE80211R */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic); +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256); + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, + u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, + const u8 *ftie, size_t ftie_len, + const u8 *rsnie, size_t rsnie_len, + const u8 *ric, size_t ric_len, u8 *mic); +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name); +void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name); +void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, + const u8 *sta_addr, const u8 *bssid, + const u8 *pmk_r1_name, + u8 *ptk, size_t ptk_len, u8 *ptk_name); +#endif /* CONFIG_IEEE80211R */ + +struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + size_t num_pmkid; + const u8 *pmkid; + int mgmt_group_cipher; +}; + + +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data); + +#endif /* WPA_COMMON_H */ diff --git a/contrib/hostapd/wpa_ctrl.c b/src/common/wpa_ctrl.c index dcec537fc7a26..2b4e3aa5ee1f7 100644 --- a/contrib/hostapd/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -62,6 +62,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; static int counter = 0; + int ret; + size_t res; + int tries = 0; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -75,18 +78,41 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) } ctrl->local.sun_family = AF_UNIX; - os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + counter++; +try_again: + ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter); + if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + tries++; if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { + if (errno == EADDRINUSE && tries < 2) { + /* + * getpid() returns unique identifier for this instance + * of wpa_ctrl, so the existing socket file must have + * been left by unclean termination of an earlier run. + * Remove the file and try again. + */ + unlink(ctrl->local.sun_path); + goto try_again; + } close(ctrl->s); os_free(ctrl); return NULL; } ctrl->dest.sun_family = AF_UNIX; - os_snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", - ctrl_path); + res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, + sizeof(ctrl->dest.sun_path)); + if (res >= sizeof(ctrl->dest.sun_path)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { close(ctrl->s); @@ -152,7 +178,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) len = sizeof(buf) - 1; if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { buf[len] = '\0'; - ctrl->cookie = strdup(buf); + ctrl->cookie = os_strdup(buf); } return ctrl; @@ -184,16 +210,16 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, #ifdef CONFIG_CTRL_IFACE_UDP if (ctrl->cookie) { char *pos; - _cmd_len = strlen(ctrl->cookie) + 1 + cmd_len; - cmd_buf = os_malloc(_cmd_len ); + _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len; + cmd_buf = os_malloc(_cmd_len); if (cmd_buf == NULL) return -1; _cmd = cmd_buf; pos = cmd_buf; - strcpy(pos, ctrl->cookie); - pos += strlen(ctrl->cookie); + os_strlcpy(pos, ctrl->cookie, _cmd_len); + pos += os_strlen(ctrl->cookie); *pos++ = ' '; - memcpy(pos, cmd, cmd_len); + os_memcpy(pos, cmd, cmd_len); } else #endif /* CONFIG_CTRL_IFACE_UDP */ { @@ -318,7 +344,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct wpa_ctrl *ctrl; DWORD mode; TCHAR name[256]; - int i; + int i, ret; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -327,17 +353,21 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) #ifdef UNICODE if (ctrl_path == NULL) - _snwprintf(name, 256, NAMED_PIPE_PREFIX); + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX); else - _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), - ctrl_path); + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + ctrl_path); #else /* UNICODE */ if (ctrl_path == NULL) - os_snprintf(name, 256, NAMED_PIPE_PREFIX); + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX); else - os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", - ctrl_path); + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + ctrl_path); #endif /* UNICODE */ + if (ret < 0 || ret >= 256) { + os_free(ctrl); + return NULL; + } for (i = 0; i < 10; i++) { ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, diff --git a/contrib/hostapd/wpa_ctrl.h b/src/common/wpa_ctrl.h index 6166fda26ee96..1bfc0d6453468 100644 --- a/contrib/hostapd/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -46,6 +46,34 @@ extern "C" { #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** New scan results available */ +#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " + +/** WPS overlap detected in PBC mode */ +#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " +/** Available WPS AP with active PBC found in scan results */ +#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with recently selected PIN registrar found in scan results + */ +#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " +/** Available WPS AP found in scan results */ +#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE " +/** A new credential received */ +#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED " +/** M2D received */ +#define WPS_EVENT_M2D "WPS-M2D " +/** WPS registration failed after M2/M2D */ +#define WPS_EVENT_FAIL "WPS-FAIL " +/** WPS registration completed successfully */ +#define WPS_EVENT_SUCCESS "WPS-SUCCESS " +/** WPS enrollment attempt timed out and was terminated */ +#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " + +/* hostapd control interface - fixed message prefixes */ +#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " +#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " +#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS " +#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED " /* wpa_supplicant/hostapd control interface access */ diff --git a/src/crypto/.gitignore b/src/crypto/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/crypto/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/crypto/Makefile b/src/crypto/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/crypto/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/aes.c b/src/crypto/aes.c index 1a2459b3e0130..8b8f2a04dee2d 100644 --- a/contrib/hostapd/aes.c +++ b/src/crypto/aes.c @@ -23,6 +23,12 @@ #include "includes.h" +#include "common.h" + +#ifdef INTERNAL_AES + +#include "crypto.h" + /* * rijndael-alg-fst.c * @@ -887,6 +893,7 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) } } +#ifndef CONFIG_NO_AES_DECRYPT /** * Expand the cipher key into the decryption key schedule. * @@ -918,7 +925,9 @@ void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) } } } +#endif /* CONFIG_NO_AES_DECRYPT */ +#ifndef CONFIG_NO_AES_ENCRYPT void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; @@ -985,6 +994,7 @@ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } +#endif /* CONFIG_NO_AES_ENCRYPT */ void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) { @@ -1057,12 +1067,15 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] /* Generic wrapper functions for AES functions */ +#define AES_PRIV_SIZE (4 * 44) + +#ifndef CONFIG_NO_AES_ENCRYPT void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; if (len != 16) return NULL; - rk = os_malloc(4 * 44); + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; rijndaelKeySetupEnc(rk, key); @@ -1078,16 +1091,19 @@ void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) void aes_encrypt_deinit(void *ctx) { + os_memset(ctx, 0, AES_PRIV_SIZE); os_free(ctx); } +#endif /* CONFIG_NO_AES_ENCRYPT */ +#ifndef CONFIG_NO_AES_DECRYPT void * aes_decrypt_init(const u8 *key, size_t len) { u32 *rk; if (len != 16) return NULL; - rk = os_malloc(4 * 44); + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; rijndaelKeySetupDec(rk, key); @@ -1103,5 +1119,9 @@ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) void aes_decrypt_deinit(void *ctx) { + os_memset(ctx, 0, AES_PRIV_SIZE); os_free(ctx); } +#endif /* CONFIG_NO_AES_DECRYPT */ + +#endif /* INTERNAL_AES */ diff --git a/contrib/hostapd/aes.h b/src/crypto/aes.h index 6b9f4147afb17..6b9f4147afb17 100644 --- a/contrib/hostapd/aes.h +++ b/src/crypto/aes.h diff --git a/contrib/hostapd/aes_wrap.c b/src/crypto/aes_wrap.c index 765b1ca871ad4..b1448b0d9a8dd 100644 --- a/contrib/hostapd/aes_wrap.c +++ b/src/crypto/aes_wrap.c @@ -2,7 +2,7 @@ * AES-based functions * * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - One-Key CBC MAC (OMAC1, i.e., CMAC) hash with AES-128 * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC @@ -25,11 +25,6 @@ #include "aes_wrap.h" #include "crypto.h" -#ifdef INTERNAL_AES -#include "aes.c" -#endif /* INTERNAL_AES */ - - #ifndef CONFIG_NO_AES_WRAP /** @@ -91,6 +86,8 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) #endif /* CONFIG_NO_AES_WRAP */ +#ifndef CONFIG_NO_AES_UNWRAP + /** * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * @kek: Key encryption key (KEK) @@ -150,6 +147,8 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) return 0; } +#endif /* CONFIG_NO_AES_UNWRAP */ + #define BLOCK_SIZE 16 @@ -176,6 +175,10 @@ static void gf_mulx(u8 *pad) * @len: Lengths of the data blocks * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. */ int omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -258,6 +261,7 @@ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) #endif /* CONFIG_NO_AES_OMAC1 */ +#ifndef CONFIG_NO_AES_ENCRYPT_BLOCK /** * aes_128_encrypt_block - Perform one AES 128-bit block operation * @key: Key for AES @@ -275,6 +279,7 @@ int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) aes_encrypt_deinit(ctx); return 0; } +#endif /* CONFIG_NO_AES_ENCRYPT_BLOCK */ #ifndef CONFIG_NO_AES_CTR @@ -344,7 +349,7 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, u8 *buf; size_t buf_len; u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i; + int i, ret = -1; if (nonce_len > data_len) buf_len = nonce_len; @@ -362,23 +367,29 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, buf[15] = 0; os_memcpy(buf + 16, nonce, nonce_len); - omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) + goto fail; buf[15] = 1; os_memcpy(buf + 16, hdr, hdr_len); - omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) + goto fail; - aes_128_ctr_encrypt(key, nonce_mac, data, data_len); + if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len)) + goto fail; buf[15] = 2; os_memcpy(buf + 16, data, data_len); - omac1_aes_128(key, buf, 16 + data_len, data_mac); - - os_free(buf); + if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) + goto fail; for (i = 0; i < BLOCK_SIZE; i++) tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; - return 0; + ret = 0; +fail: + os_free(buf); + + return ret; } @@ -419,15 +430,24 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, buf[15] = 0; os_memcpy(buf + 16, nonce, nonce_len); - omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) { + os_free(buf); + return -1; + } buf[15] = 1; os_memcpy(buf + 16, hdr, hdr_len); - omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) { + os_free(buf); + return -1; + } buf[15] = 2; os_memcpy(buf + 16, data, data_len); - omac1_aes_128(key, buf, 16 + data_len, data_mac); + if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) { + os_free(buf); + return -1; + } os_free(buf); @@ -436,9 +456,7 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, return -2; } - aes_128_ctr_encrypt(key, nonce_mac, data, data_len); - - return 0; + return aes_128_ctr_encrypt(key, nonce_mac, data, data_len); } #endif /* CONFIG_NO_AES_EAX */ diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h new file mode 100644 index 0000000000000..4b1c7b083b334 --- /dev/null +++ b/src/crypto/aes_wrap.h @@ -0,0 +1,48 @@ +/* + * 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-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_WRAP_H +#define AES_WRAP_H + +int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac); +int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, + u8 *mac); +int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int __must_check aes_128_eax_encrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int __must_check aes_128_eax_decrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +#endif /* AES_WRAP_H */ diff --git a/contrib/hostapd/crypto.h b/src/crypto/crypto.h index 00b13b91c48e1..a5129bbd050ae 100644 --- a/contrib/hostapd/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -67,7 +67,8 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to * SHA-1, but has different message padding. */ -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen); +int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, + size_t xlen); /** * sha256_vector - SHA256 hash for data vector @@ -222,8 +223,8 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len); +int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, + const u8 *plain, u8 *crypt, size_t len); /** * crypto_cipher_decrypt - Cipher decrypt @@ -237,8 +238,8 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len); +int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, + const u8 *crypt, u8 *plain, size_t len); /** * crypto_cipher_decrypt - Free cipher context @@ -313,9 +314,26 @@ struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen); +int __must_check crypto_public_key_encrypt_pkcs1_v15( + struct crypto_public_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5) + * @key: Private key + * @in: Encrypted buffer + * @inlen: Length of encrypted buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_decrypt_pkcs1_v15( + struct crypto_private_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); /** * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1) @@ -330,9 +348,9 @@ int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen); +int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); /** * crypto_public_key_free - Free public key @@ -363,9 +381,9 @@ void crypto_private_key_free(struct crypto_private_key *key); * @plain_len: Plaintext length (max buffer size on input, real len on output); * Returns: 0 on success, -1 on failure */ -int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, - const u8 *crypt, size_t crypt_len, - u8 *plain, size_t *plain_len); +int __must_check crypto_public_key_decrypt_pkcs1( + struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); /** * crypto_global_init - Initialize crypto wrapper @@ -374,7 +392,7 @@ int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_global_init(void); +int __must_check crypto_global_init(void); /** * crypto_global_deinit - Deinitialize crypto wrapper @@ -405,9 +423,9 @@ void crypto_global_deinit(void); * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need * to implement this. */ -int crypto_mod_exp(const u8 *base, size_t base_len, - const u8 *power, size_t power_len, - const u8 *modulus, size_t modulus_len, - u8 *result, size_t *result_len); +int __must_check crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len); #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c new file mode 100644 index 0000000000000..bb0573078d244 --- /dev/null +++ b/src/crypto/crypto_cryptoapi.c @@ -0,0 +1,801 @@ +/* + * WPA Supplicant / Crypto wrapper for Microsoft CryptoAPI + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <windows.h> +#include <wincrypt.h> + +#include "common.h" +#include "crypto.h" + +#ifndef MS_ENH_RSA_AES_PROV +#ifdef UNICODE +#define MS_ENH_RSA_AES_PROV \ +L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" +#else +#define MS_ENH_RSA_AES_PROV \ +"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" +#endif +#endif /* MS_ENH_RSA_AES_PROV */ + +#ifndef CALG_HMAC +#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) +#endif + +#ifdef CONFIG_TLS_INTERNAL +#ifdef __MINGW32_VERSION +/* + * MinGW does not yet include all the needed definitions for CryptoAPI, so + * define here whatever extra is needed. + */ + +static PCCERT_CONTEXT WINAPI +(*CertCreateCertificateContext)(DWORD dwCertEncodingType, + const BYTE *pbCertEncoded, + DWORD cbCertEncoded) += NULL; /* to be loaded from crypt32.dll */ + +static BOOL WINAPI +(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) += NULL; /* to be loaded from crypt32.dll */ + + +static int mingw_load_crypto_func(void) +{ + HINSTANCE dll; + + /* MinGW does not yet have full CryptoAPI support, so load the needed + * function here. */ + + if (CertCreateCertificateContext) + return 0; + + dll = LoadLibrary("crypt32"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " + "library"); + return -1; + } + + CertCreateCertificateContext = (void *) GetProcAddress( + dll, "CertCreateCertificateContext"); + if (CertCreateCertificateContext == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CertCreateCertificateContext() address from " + "crypt32 library"); + return -1; + } + + CryptImportPublicKeyInfo = GetProcAddress( + dll, "CryptImportPublicKeyInfo"); + if (CryptImportPublicKeyInfo == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CryptImportPublicKeyInfo() address from " + "crypt32 library"); + return -1; + } + + return 0; +} + +#else /* __MINGW32_VERSION */ + +static int mingw_load_crypto_func(void) +{ + return 0; +} + +#endif /* __MINGW32_VERSION */ +#endif /* CONFIG_TLS_INTERNAL */ + + +static void cryptoapi_report_error(const char *msg) +{ + char *s, *pos; + DWORD err = GetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { + wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); + } + + pos = s; + while (*pos) { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); + LocalFree(s); +} + + +int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HCRYPTPROV prov; + HCRYPTHASH hash; + size_t i; + DWORD hlen; + int ret = 0; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { + cryptoapi_report_error("CryptAcquireContext"); + return -1; + } + + if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { + cryptoapi_report_error("CryptCreateHash"); + CryptReleaseContext(prov, 0); + return -1; + } + + for (i = 0; i < num_elem; i++) { + if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { + cryptoapi_report_error("CryptHashData"); + CryptDestroyHash(hash); + CryptReleaseContext(prov, 0); + } + } + + hlen = hash_len; + if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { + cryptoapi_report_error("CryptGetHashParam"); + ret = -1; + } + + CryptDestroyHash(hash); + CryptReleaseContext(prov, 0); + + return ret; +} + + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 next, tmp; + int i; + HCRYPTPROV prov; + HCRYPTKEY ckey; + DWORD dlen; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[8]; + } key_blob; + DWORD mode = CRYPT_MODE_ECB; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.hdr.aiKeyAlg = CALG_DES; + key_blob.len = 8; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + key_blob.key[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + key_blob.key[i] = next | 1; + + if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " + "%d", (int) GetLastError()); + return; + } + + if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, + &ckey)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", + (int) GetLastError()); + CryptReleaseContext(prov, 0); + return; + } + + if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " + "failed: %d", (int) GetLastError()); + CryptDestroyKey(ckey); + CryptReleaseContext(prov, 0); + return; + } + + os_memcpy(cypher, clear, 8); + dlen = 8; + if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", + (int) GetLastError()); + os_memset(cypher, 0, 8); + } + + CryptDestroyKey(ckey); + CryptReleaseContext(prov, 0); +} + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); +} + + +struct aes_context { + HCRYPTPROV prov; + HCRYPTKEY ckey; +}; + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + struct aes_context *akey; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[16]; + } key_blob; + DWORD mode = CRYPT_MODE_ECB; + + if (len != 16) + return NULL; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.hdr.aiKeyAlg = CALG_AES_128; + key_blob.len = len; + os_memcpy(key_blob.key, key, len); + + akey = os_zalloc(sizeof(*akey)); + if (akey == NULL) + return NULL; + + if (!CryptAcquireContext(&akey->prov, NULL, + MS_ENH_RSA_AES_PROV, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " + "%d", (int) GetLastError()); + os_free(akey); + return NULL; + } + + if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), + 0, 0, &akey->ckey)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", + (int) GetLastError()); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + return NULL; + } + + if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " + "failed: %d", (int) GetLastError()); + CryptDestroyKey(akey->ckey); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + return NULL; + } + + return akey; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct aes_context *akey = ctx; + DWORD dlen; + + os_memcpy(crypt, plain, 16); + dlen = 16; + if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", + (int) GetLastError()); + os_memset(crypt, 0, 16); + } +} + + +void aes_encrypt_deinit(void *ctx) +{ + struct aes_context *akey = ctx; + if (akey) { + CryptDestroyKey(akey->ckey); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + } +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return aes_encrypt_init(key, len); +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct aes_context *akey = ctx; + DWORD dlen; + + os_memcpy(plain, crypt, 16); + dlen = 16; + + if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", + (int) GetLastError()); + } +} + + +void aes_decrypt_deinit(void *ctx) +{ + aes_encrypt_deinit(ctx); +} + +#ifdef CONFIG_TLS_INTERNAL + +struct crypto_hash { + enum crypto_hash_alg alg; + int error; + HCRYPTPROV prov; + HCRYPTHASH hash; + HCRYPTKEY key; +}; + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + ALG_ID calg; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[32]; + } key_blob; + + os_memset(&key_blob, 0, sizeof(key_blob)); + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + calg = CALG_MD5; + break; + case CRYPTO_HASH_ALG_SHA1: + calg = CALG_SHA; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + case CRYPTO_HASH_ALG_HMAC_SHA1: + calg = CALG_HMAC; + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + /* + * Note: RC2 is not really used, but that can be used to + * import HMAC keys of up to 16 byte long. + * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to + * be able to import longer keys (HMAC-SHA1 uses 20-byte key). + */ + key_blob.hdr.aiKeyAlg = CALG_RC2; + key_blob.len = key_len; + if (key_len > sizeof(key_blob.key)) + return NULL; + os_memcpy(key_blob.key, key, key_len); + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { + cryptoapi_report_error("CryptAcquireContext"); + os_free(ctx); + return NULL; + } + + if (calg == CALG_HMAC) { +#ifndef CRYPT_IPSEC_HMAC_KEY +#define CRYPT_IPSEC_HMAC_KEY 0x00000100 +#endif + if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, + sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, + &ctx->key)) { + cryptoapi_report_error("CryptImportKey"); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + } + + if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { + cryptoapi_report_error("CryptCreateHash"); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + + if (calg == CALG_HMAC) { + HMAC_INFO info; + os_memset(&info, 0, sizeof(info)); + switch (alg) { + case CRYPTO_HASH_ALG_HMAC_MD5: + info.HashAlgid = CALG_MD5; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + info.HashAlgid = CALG_SHA; + break; + default: + /* unreachable */ + break; + } + + if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, + 0)) { + cryptoapi_report_error("CryptSetHashParam"); + CryptDestroyHash(ctx->hash); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL || ctx->error) + return; + + if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { + cryptoapi_report_error("CryptHashData"); + ctx->error = 1; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + DWORD hlen; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) + goto done; + + if (ctx->error) { + ret = -2; + goto done; + } + + hlen = *len; + if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { + cryptoapi_report_error("CryptGetHashParam"); + ret = -2; + } + *len = hlen; + +done: + if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || + ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) + CryptDestroyKey(ctx->key); + + os_free(ctx); + + return ret; +} + + +struct crypto_cipher { + HCRYPTPROV prov; + HCRYPTKEY key; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[32]; + } key_blob; + DWORD mode = CRYPT_MODE_CBC; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.len = key_len; + if (key_len > sizeof(key_blob.key)) + return NULL; + os_memcpy(key_blob.key, key, key_len); + + switch (alg) { + case CRYPTO_CIPHER_ALG_AES: + if (key_len == 32) + key_blob.hdr.aiKeyAlg = CALG_AES_256; + else if (key_len == 24) + key_blob.hdr.aiKeyAlg = CALG_AES_192; + else + key_blob.hdr.aiKeyAlg = CALG_AES_128; + break; + case CRYPTO_CIPHER_ALG_3DES: + key_blob.hdr.aiKeyAlg = CALG_3DES; + break; + case CRYPTO_CIPHER_ALG_DES: + key_blob.hdr.aiKeyAlg = CALG_DES; + break; + case CRYPTO_CIPHER_ALG_RC2: + key_blob.hdr.aiKeyAlg = CALG_RC2; + break; + case CRYPTO_CIPHER_ALG_RC4: + key_blob.hdr.aiKeyAlg = CALG_RC4; + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + cryptoapi_report_error("CryptAcquireContext"); + goto fail1; + } + + if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, + sizeof(key_blob), 0, 0, &ctx->key)) { + cryptoapi_report_error("CryptImportKey"); + goto fail2; + } + + if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { + cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); + goto fail3; + } + + if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { + cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); + goto fail3; + } + + return ctx; + +fail3: + CryptDestroyKey(ctx->key); +fail2: + CryptReleaseContext(ctx->prov, 0); +fail1: + os_free(ctx); + return NULL; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + DWORD dlen; + + os_memcpy(crypt, plain, len); + dlen = len; + if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { + cryptoapi_report_error("CryptEncrypt"); + os_memset(crypt, 0, len); + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + DWORD dlen; + + os_memcpy(plain, crypt, len); + dlen = len; + if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { + cryptoapi_report_error("CryptDecrypt"); + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + CryptDestroyKey(ctx->key); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); +} + + +struct crypto_public_key { + HCRYPTPROV prov; + HCRYPTKEY rsa; +}; + +struct crypto_private_key { + HCRYPTPROV prov; + HCRYPTKEY rsa; +}; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + /* Use crypto_public_key_from_cert() instead. */ + return NULL; +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len) +{ + /* TODO */ + return NULL; +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + struct crypto_public_key *pk; + PCCERT_CONTEXT cc; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + cc = CertCreateCertificateContext(X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, buf, len); + if (!cc) { + cryptoapi_report_error("CryptCreateCertificateContext"); + os_free(pk); + return NULL; + } + + if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, + 0)) { + cryptoapi_report_error("CryptAcquireContext"); + os_free(pk); + CertFreeCertificateContext(cc); + return NULL; + } + + if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + &cc->pCertInfo->SubjectPublicKeyInfo, + &pk->rsa)) { + cryptoapi_report_error("CryptImportPublicKeyInfo"); + CryptReleaseContext(pk->prov, 0); + os_free(pk); + CertFreeCertificateContext(cc); + return NULL; + } + + CertFreeCertificateContext(cc); + + return pk; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + DWORD clen; + u8 *tmp; + size_t i; + + if (*outlen < inlen) + return -1; + tmp = malloc(*outlen); + if (tmp == NULL) + return -1; + + os_memcpy(tmp, in, inlen); + clen = inlen; + if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " + "public key: %d", (int) GetLastError()); + os_free(tmp); + return -1; + } + + *outlen = clen; + + /* Reverse the output */ + for (i = 0; i < *outlen; i++) + out[i] = tmp[*outlen - 1 - i]; + + os_free(tmp); + + return 0; +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + /* TODO */ + return -1; +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + if (key) { + CryptDestroyKey(key->rsa); + CryptReleaseContext(key->prov, 0); + os_free(key); + } +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + if (key) { + CryptDestroyKey(key->rsa); + CryptReleaseContext(key->prov, 0); + os_free(key); + } +} + + +int crypto_global_init(void) +{ + return mingw_load_crypto_func(); +} + + +void crypto_global_deinit(void) +{ +} + +#endif /* CONFIG_TLS_INTERNAL */ + +#endif /* EAP_TLS_FUNCS */ diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c new file mode 100644 index 0000000000000..8023965a07732 --- /dev/null +++ b/src/crypto/crypto_gnutls.c @@ -0,0 +1,313 @@ +/* + * WPA Supplicant / wrapper functions for libgcrypt + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <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; + size_t 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; + size_t 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; + size_t 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); +} + + +#ifndef CONFIG_NO_FIPS186_2_PRF +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + /* FIX: how to do this with libgcrypt? */ + return -1; +} +#endif /* CONFIG_NO_FIPS186_2_PRF */ + + +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 */ + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL, + bn_result = NULL; + int ret = -1; + + if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len, + NULL) != GPG_ERR_NO_ERROR) + goto error; + bn_result = gcry_mpi_new(modulus_len * 8); + + gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus); + + if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len, + bn_result) != GPG_ERR_NO_ERROR) + goto error; + + ret = 0; + +error: + gcry_mpi_release(bn_base); + gcry_mpi_release(bn_exp); + gcry_mpi_release(bn_modulus); + gcry_mpi_release(bn_result); + return ret; +} + + +struct crypto_cipher { + gcry_cipher_hd_t enc; + gcry_cipher_hd_t dec; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + gcry_error_t res; + enum gcry_cipher_algos a; + int ivlen; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + a = GCRY_CIPHER_ARCFOUR; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM, + 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0); + break; + case CRYPTO_CIPHER_ALG_AES: + if (key_len == 24) + a = GCRY_CIPHER_AES192; + else if (key_len == 32) + a = GCRY_CIPHER_AES256; + else + a = GCRY_CIPHER_AES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_3DES: + a = GCRY_CIPHER_3DES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_DES: + a = GCRY_CIPHER_DES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_RC2: + if (key_len == 5) + a = GCRY_CIPHER_RFC2268_40; + else + a = GCRY_CIPHER_RFC2268_128; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + default: + os_free(ctx); + return NULL; + } + + if (res != GPG_ERR_NO_ERROR) { + os_free(ctx); + return NULL; + } + + if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR || + gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); + return NULL; + } + + ivlen = gcry_cipher_get_algo_blklen(a); + if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR || + gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) != + GPG_ERR_NO_ERROR) + return -1; + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) != + GPG_ERR_NO_ERROR) + return -1; + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); +} diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c new file mode 100644 index 0000000000000..a601cbf937b7a --- /dev/null +++ b/src/crypto/crypto_internal.c @@ -0,0 +1,835 @@ +/* + * WPA Supplicant / Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "md5.h" +#include "sha1.h" +#include "rc4.h" +#include "aes.h" +#include "tls/rsa.h" +#include "tls/bignum.h" +#include "tls/asn1.h" + + +#ifdef EAP_TLS_FUNCS + +#ifdef CONFIG_TLS_INTERNAL + +/* from des.c */ +struct des3_key_s { + u32 ek[3][32]; + u32 dk[3][32]; +}; + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey); +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); + + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +struct SHA1Context { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +}; + + +struct crypto_hash { + enum crypto_hash_alg alg; + union { + struct MD5Context md5; + struct SHA1Context sha1; + } u; + u8 key[64]; + size_t key_len; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + u8 k_pad[64]; + u8 tk[20]; + size_t i; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + MD5Init(&ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + SHA1Init(&ctx->u.sha1); + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + if (key_len > sizeof(k_pad)) { + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, key, key_len); + MD5Final(tk, &ctx->u.md5); + key = tk; + key_len = 16; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (key_len > sizeof(k_pad)) { + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, key, key_len); + SHA1Final(tk, &ctx->u.sha1); + key = tk; + key_len = 20; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + MD5Update(&ctx->u.md5, data, len); + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + SHA1Update(&ctx->u.sha1, data, len); + break; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + u8 k_pad[64]; + size_t i; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + SHA1Final(mac, &ctx->u.sha1); + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + + MD5Final(mac, &ctx->u.md5); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + MD5Update(&ctx->u.md5, mac, 16); + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + + SHA1Final(mac, &ctx->u.sha1); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + SHA1Update(&ctx->u.sha1, mac, 20); + SHA1Final(mac, &ctx->u.sha1); + break; + } + + os_free(ctx); + + return 0; +} + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + struct { + u8 cbc[32]; + size_t block_size; + void *ctx_enc; + void *ctx_dec; + } aes; + struct { + struct des3_key_s key; + u8 cbc[8]; + } des3; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + break; + case CRYPTO_CIPHER_ALG_AES: + if (key_len > sizeof(ctx->u.aes.cbc)) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); + if (ctx->u.aes.ctx_enc == NULL) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); + if (ctx->u.aes.ctx_dec == NULL) { + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + os_free(ctx); + return NULL; + } + ctx->u.aes.block_size = key_len; + os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size); + break; + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != 24) { + os_free(ctx); + return NULL; + } + des3_key_setup(key, &ctx->u.des3.key); + os_memcpy(ctx->u.des3.cbc, iv, 8); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + size_t i, j, blocks; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % ctx->u.aes.block_size) + return -1; + blocks = len / ctx->u.aes.block_size; + for (i = 0; i < blocks; i++) { + for (j = 0; j < ctx->u.aes.block_size; j++) + ctx->u.aes.cbc[j] ^= plain[j]; + aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, + ctx->u.aes.cbc); + os_memcpy(crypt, ctx->u.aes.cbc, + ctx->u.aes.block_size); + plain += ctx->u.aes.block_size; + crypt += ctx->u.aes.block_size; + } + break; + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, + ctx->u.des3.cbc); + os_memcpy(crypt, ctx->u.des3.cbc, 8); + plain += 8; + crypt += 8; + } + break; + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + size_t i, j, blocks; + u8 tmp[32]; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % ctx->u.aes.block_size) + return -1; + blocks = len / ctx->u.aes.block_size; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, ctx->u.aes.block_size); + aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); + for (j = 0; j < ctx->u.aes.block_size; j++) + plain[j] ^= ctx->u.aes.cbc[j]; + os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size); + plain += ctx->u.aes.block_size; + crypt += ctx->u.aes.block_size; + } + break; + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des3_decrypt(crypt, &ctx->u.des3.key, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des3.cbc[j]; + os_memcpy(ctx->u.des3.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_AES: + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + aes_decrypt_deinit(ctx->u.aes.ctx_dec); + break; + case CRYPTO_CIPHER_ALG_3DES: + break; + default: + break; + } + os_free(ctx); +} + + +/* Dummy structures; these are just typecast to struct crypto_rsa_key */ +struct crypto_public_key; +struct crypto_private_key; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + return (struct crypto_public_key *) + crypto_rsa_import_public_key(key, len); +} + + +static struct crypto_private_key * +crypto_pkcs8_key_import(const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct bignum *zero; + struct asn1_oid oid; + char obuf[80]; + + /* PKCS #8, Chapter 6 */ + + /* PrivateKeyInfo ::= SEQUENCE */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " + "header (SEQUENCE); assume PKCS #8 not used"); + return NULL; + } + pos = hdr.payload; + end = pos + hdr.length; + + /* version Version (Version ::= INTEGER) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " + "class %d tag 0x%x; assume PKCS #8 not used", + hdr.class, hdr.tag); + return NULL; + } + + zero = bignum_init(); + if (zero == NULL) + return NULL; + + if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER"); + bignum_deinit(zero); + return NULL; + } + pos = hdr.payload + hdr.length; + + if (bignum_cmp_d(zero, 0) != 0) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the " + "beginning of private key; not found; assume " + "PKCS #8 not used"); + bignum_deinit(zero); + return NULL; + } + bignum_deinit(zero); + + /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier + * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x; " + "assume PKCS #8 not used", + hdr.class, hdr.tag); + return NULL; + } + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID " + "(algorithm); assume PKCS #8 not used"); + return NULL; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf); + + if (oid.len != 7 || + oid.oid[0] != 1 /* iso */ || + oid.oid[1] != 2 /* member-body */ || + oid.oid[2] != 840 /* us */ || + oid.oid[3] != 113549 /* rsadsi */ || + oid.oid[4] != 1 /* pkcs */ || + oid.oid[5] != 1 /* pkcs-1 */ || + oid.oid[6] != 1 /* rsaEncryption */) { + wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key " + "algorithm %s", obuf); + return NULL; + } + + pos = hdr.payload + hdr.length; + + /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " + "(privateKey) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return NULL; + } + wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); + + return (struct crypto_private_key *) + crypto_rsa_import_private_key(hdr.payload, hdr.length); +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len) +{ + struct crypto_private_key *res; + + /* First, check for possible PKCS #8 encoding */ + res = crypto_pkcs8_key_import(key, len); + if (res) + return res; + + /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ + wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " + "key"); + return (struct crypto_private_key *) + crypto_rsa_import_private_key(key, len); +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in crypto_internal.c */ + return NULL; +} + + +static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t ps_len; + u8 *pos; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 for private-key operation; 02 for public-key operation + * PS = k-3-||D||; at least eight octets + * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) + * k = length of modulus in octets (modlen) + */ + + if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " + "lengths (modlen=%lu outlen=%lu inlen=%lu)", + __func__, (unsigned long) modlen, + (unsigned long) *outlen, + (unsigned long) inlen); + return -1; + } + + pos = out; + *pos++ = 0x00; + *pos++ = block_type; /* BT */ + ps_len = modlen - inlen - 3; + switch (block_type) { + case 0: + os_memset(pos, 0x00, ps_len); + pos += ps_len; + break; + case 1: + os_memset(pos, 0xff, ps_len); + pos += ps_len; + break; + case 2: + if (os_get_random(pos, ps_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " + "random data for PS", __func__); + return -1; + } + while (ps_len--) { + if (*pos == 0x00) + *pos = 0x01; + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " + "%d", __func__, block_type); + return -1; + } + *pos++ = 0x00; + os_memcpy(pos, in, inlen); /* D */ + + return 0; +} + + +static int crypto_rsa_encrypt_pkcs1(int block_type, struct crypto_rsa_key *key, + int use_private, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t modlen; + + modlen = crypto_rsa_get_modulus_len(key); + + if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, + out, outlen) < 0) + return -1; + + return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private); +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(2, (struct crypto_rsa_key *) key, + 0, in, inlen, out, outlen); +} + + +int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + struct crypto_rsa_key *rkey = (struct crypto_rsa_key *) key; + int res; + u8 *pos, *end; + + res = crypto_rsa_exptmod(in, inlen, out, outlen, rkey, 1); + if (res) + return res; + + if (*outlen < 2 || out[0] != 0 || out[1] != 2) + return -1; + + /* Skip PS (pseudorandom non-zero octets) */ + pos = out + 2; + end = out + *outlen; + while (*pos && pos < end) + pos++; + if (pos == end) + return -1; + pos++; + + *outlen -= pos - out; + + /* Strip PKCS #1 header */ + os_memmove(out, pos, *outlen); + + return 0; +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(1, (struct crypto_rsa_key *) key, + 1, in, inlen, out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + size_t len; + u8 *pos; + + len = *plain_len; + if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, + (struct crypto_rsa_key *) key, 0) < 0) + return -1; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 + * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) + * k = length of modulus in octets + */ + + if (len < 3 + 8 + 16 /* min hash len */ || + plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure"); + return -1; + } + + pos = plain + 3; + if (plain[1] == 0x00) { + /* BT = 00 */ + if (plain[2] != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=00)"); + return -1; + } + while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) + pos++; + } else { + /* BT = 01 */ + if (plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=01)"); + return -1; + } + while (pos < plain + len && *pos == 0xff) + pos++; + } + + if (pos - plain - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " + "padding"); + return -1; + } + + if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure (2)"); + return -1; + } + pos++; + len -= pos - plain; + + /* Strip PKCS #1 header */ + os_memmove(plain, pos, len); + *plain_len = len; + + return 0; +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} + + +#if defined(EAP_FAST) || defined(CONFIG_WPS) + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result; + int ret = -1; + + bn_base = bignum_init(); + bn_exp = bignum_init(); + bn_modulus = bignum_init(); + bn_result = bignum_init(); + + if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || + bn_result == NULL) + goto error; + + if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 || + bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 || + bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0) + goto error; + + if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0) + goto error; + + ret = bignum_get_unsigned_bin(bn_result, result, result_len); + +error: + bignum_deinit(bn_base); + bignum_deinit(bn_exp); + bignum_deinit(bn_modulus); + bignum_deinit(bn_result); + return ret; +} + +#endif /* EAP_FAST || CONFIG_WPS */ + + +#endif /* CONFIG_TLS_INTERNAL */ + +#endif /* EAP_TLS_FUNCS */ diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c new file mode 100644 index 0000000000000..e82097f10a00f --- /dev/null +++ b/src/crypto/crypto_libtomcrypt.c @@ -0,0 +1,736 @@ +/* + * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <tomcrypt.h> + +#include "common.h" +#include "rc4.h" +#include "crypto.h" + +#ifndef mp_init_multi +#define mp_init_multi ltc_init_multi +#define mp_clear_multi ltc_deinit_multi +#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) +#define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d) +#endif + + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + md4_init(&md); + for (i = 0; i < num_elem; i++) + md4_process(&md, addr[i], len[i]); + md4_done(&md, mac); +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + symmetric_key skey; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + des_setup(pkey, 8, 0, &skey); + des_ecb_encrypt(clear, cypher, &skey); + des_done(&skey); +} + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + md5_init(&md); + for (i = 0; i < num_elem; i++) + md5_process(&md, addr[i], len[i]); + md5_done(&md, mac); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + sha1_init(&md); + for (i = 0; i < num_elem; i++) + sha1_process(&md, addr[i], len[i]); + sha1_done(&md, mac); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + symmetric_key *skey; + skey = os_malloc(sizeof(*skey)); + if (skey == NULL) + return NULL; + if (aes_setup(key, len, 0, skey) != CRYPT_OK) { + os_free(skey); + return NULL; + } + return skey; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + symmetric_key *skey = ctx; + aes_ecb_encrypt(plain, crypt, skey); +} + + +void aes_encrypt_deinit(void *ctx) +{ + symmetric_key *skey = ctx; + aes_done(skey); + os_free(skey); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + symmetric_key *skey; + skey = os_malloc(sizeof(*skey)); + if (skey == NULL) + return NULL; + if (aes_setup(key, len, 0, skey) != CRYPT_OK) { + os_free(skey); + return NULL; + } + return skey; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + symmetric_key *skey = ctx; + aes_ecb_encrypt(plain, (u8 *) crypt, skey); +} + + +void aes_decrypt_deinit(void *ctx) +{ + symmetric_key *skey = ctx; + aes_done(skey); + os_free(skey); +} + + +#ifdef CONFIG_TLS_INTERNAL + +struct crypto_hash { + enum crypto_hash_alg alg; + int error; + union { + hash_state md; + hmac_state hmac; + } u; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + if (md5_init(&ctx->u.md) != CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_SHA1: + if (sha1_init(&ctx->u.md) != CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) != + CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) != + CRYPT_OK) + goto fail; + break; + default: + goto fail; + } + + return ctx; + +fail: + os_free(ctx); + return NULL; +} + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL || ctx->error) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK; + break; + case CRYPTO_HASH_ALG_SHA1: + ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + case CRYPTO_HASH_ALG_HMAC_SHA1: + ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK; + break; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + unsigned long clen; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + if (ctx->error) { + os_free(ctx); + return -2; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + if (md5_done(&ctx->u.md, mac) != CRYPT_OK) + ret = -2; + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + if (sha1_done(&ctx->u.md, mac) != CRYPT_OK) + ret = -2; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + /* continue */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + clen = *len; + if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) { + os_free(ctx); + return -1; + } + *len = clen; + break; + default: + ret = -2; + break; + } + + os_free(ctx); + + return ret; +} + + +struct crypto_cipher { + int rc4; + union { + symmetric_CBC cbc; + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + int idx, res, rc4 = 0; + + switch (alg) { + case CRYPTO_CIPHER_ALG_AES: + idx = find_cipher("aes"); + break; + case CRYPTO_CIPHER_ALG_3DES: + idx = find_cipher("3des"); + break; + case CRYPTO_CIPHER_ALG_DES: + idx = find_cipher("des"); + break; + case CRYPTO_CIPHER_ALG_RC2: + idx = find_cipher("rc2"); + break; + case CRYPTO_CIPHER_ALG_RC4: + idx = -1; + rc4 = 1; + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + if (rc4) { + ctx->rc4 = 1; + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + } else { + res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start " + "failed: %s", error_to_string(res)); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + int res; + + if (ctx->rc4) { + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + return 0; + } + + res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption " + "failed: %s", error_to_string(res)); + return -1; + } + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + int res; + + if (ctx->rc4) { + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + return 0; + } + + res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption " + "failed: %s", error_to_string(res)); + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + if (!ctx->rc4) + cbc_done(&ctx->u.cbc); + os_free(ctx); +} + + +struct crypto_public_key { + rsa_key rsa; +}; + +struct crypto_private_key { + rsa_key rsa; +}; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + int res; + struct crypto_public_key *pk; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + res = rsa_import(key, len, &pk->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " + "public key (res=%d '%s')", + res, error_to_string(res)); + os_free(pk); + return NULL; + } + + if (pk->rsa.type != PK_PUBLIC) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of " + "correct type"); + rsa_free(&pk->rsa); + os_free(pk); + return NULL; + } + + return pk; +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len) +{ + int res; + struct crypto_private_key *pk; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + res = rsa_import(key, len, &pk->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " + "private key (res=%d '%s')", + res, error_to_string(res)); + os_free(pk); + return NULL; + } + + if (pk->rsa.type != PK_PRIVATE) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of " + "correct type"); + rsa_free(&pk->rsa); + os_free(pk); + return NULL; + } + + return pk; +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in LibTomCrypt */ + return NULL; +} + + +static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t ps_len; + u8 *pos; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 for private-key operation; 02 for public-key operation + * PS = k-3-||D||; at least eight octets + * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) + * k = length of modulus in octets (modlen) + */ + + if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " + "lengths (modlen=%lu outlen=%lu inlen=%lu)", + __func__, (unsigned long) modlen, + (unsigned long) *outlen, + (unsigned long) inlen); + return -1; + } + + pos = out; + *pos++ = 0x00; + *pos++ = block_type; /* BT */ + ps_len = modlen - inlen - 3; + switch (block_type) { + case 0: + os_memset(pos, 0x00, ps_len); + pos += ps_len; + break; + case 1: + os_memset(pos, 0xff, ps_len); + pos += ps_len; + break; + case 2: + if (os_get_random(pos, ps_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " + "random data for PS", __func__); + return -1; + } + while (ps_len--) { + if (*pos == 0x00) + *pos = 0x01; + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " + "%d", __func__, block_type); + return -1; + } + *pos++ = 0x00; + os_memcpy(pos, in, inlen); /* D */ + + return 0; +} + + +static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + unsigned long len, modlen; + int res; + + modlen = mp_unsigned_bin_size(key->N); + + if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, + out, outlen) < 0) + return -1; + + len = *outlen; + res = rsa_exptmod(out, modlen, out, &len, key_type, key); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", + error_to_string(res)); + return -1; + } + *outlen = len; + + return 0; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen, + out, outlen); +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen, + out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + if (key) { + rsa_free(&key->rsa); + os_free(key); + } +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + if (key) { + rsa_free(&key->rsa); + os_free(key); + } +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + int res; + unsigned long len; + u8 *pos; + + len = *plain_len; + res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC, + &key->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", + error_to_string(res)); + return -1; + } + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 01 + * PS = k-3-||D|| times FF + * k = length of modulus in octets + */ + + if (len < 3 + 8 + 16 /* min hash len */ || + plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure"); + return -1; + } + + pos = plain + 3; + while (pos < plain + len && *pos == 0xff) + pos++; + if (pos - plain - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " + "padding"); + return -1; + } + + if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure (2)"); + return -1; + } + pos++; + len -= pos - plain; + + /* Strip PKCS #1 header */ + os_memmove(plain, pos, len); + *plain_len = len; + + return 0; +} + + +int crypto_global_init(void) +{ + ltc_mp = tfm_desc; + /* TODO: only register algorithms that are really needed */ + if (register_hash(&md4_desc) < 0 || + register_hash(&md5_desc) < 0 || + register_hash(&sha1_desc) < 0 || + register_cipher(&aes_desc) < 0 || + register_cipher(&des_desc) < 0 || + register_cipher(&des3_desc) < 0) { + wpa_printf(MSG_ERROR, "TLSv1: Failed to register " + "hash/cipher functions"); + return -1; + } + + return 0; +} + + +void crypto_global_deinit(void) +{ +} + + +#ifdef EAP_FAST + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + void *b, *p, *m, *r; + + if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK) + return -1; + + if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK || + mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK || + mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK) + goto fail; + + if (mp_exptmod(b, p, m, r) != CRYPT_OK) + goto fail; + + *result_len = mp_unsigned_bin_size(r); + if (mp_to_unsigned_bin(r, result) != CRYPT_OK) + goto fail; + + mp_clear_multi(b, p, m, r, NULL); + return 0; + +fail: + mp_clear_multi(b, p, m, r, NULL); + return -1; +} + +#endif /* EAP_FAST */ + +#endif /* CONFIG_TLS_INTERNAL */ + +#endif /* EAP_TLS_FUNCS */ diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c new file mode 100644 index 0000000000000..f18c2a8ded95c --- /dev/null +++ b/src/crypto/crypto_none.c @@ -0,0 +1,28 @@ +/* + * WPA Supplicant / Empty template functions for crypto wrapper + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ +} diff --git a/contrib/hostapd/crypto.c b/src/crypto/crypto_openssl.c index c5edd24c4b550..a4c3415c4d18b 100644 --- a/contrib/hostapd/crypto.c +++ b/src/crypto/crypto_openssl.c @@ -19,6 +19,8 @@ #include <openssl/sha.h> #include <openssl/des.h> #include <openssl/aes.h> +#include <openssl/bn.h> +#include <openssl/evp.h> #include "common.h" #include "crypto.h" @@ -65,7 +67,6 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD5_CTX ctx; @@ -90,6 +91,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } +#ifndef CONFIG_NO_FIPS186_2_PRF static void sha1_transform(u8 *state, const u8 data[64]) { SHA_CTX context; @@ -152,6 +154,7 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) return 0; } +#endif /* CONFIG_NO_FIPS186_2_PRF */ void * aes_encrypt_init(const u8 *key, size_t len) @@ -204,4 +207,154 @@ void aes_decrypt_deinit(void *ctx) { os_free(ctx); } -#endif /* EAP_TLS_FUNCS */ + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result; + int ret = -1; + BN_CTX *ctx; + + ctx = BN_CTX_new(); + if (ctx == NULL) + return -1; + + bn_base = BN_bin2bn(base, base_len, NULL); + bn_exp = BN_bin2bn(power, power_len, NULL); + bn_modulus = BN_bin2bn(modulus, modulus_len, NULL); + bn_result = BN_new(); + + if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || + bn_result == NULL) + goto error; + + if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + goto error; + + *result_len = BN_bn2bin(bn_result, result); + ret = 0; + +error: + BN_free(bn_base); + BN_free(bn_exp); + BN_free(bn_modulus); + BN_free(bn_result); + BN_CTX_free(ctx); + return ret; +} + + +struct crypto_cipher { + EVP_CIPHER_CTX enc; + EVP_CIPHER_CTX dec; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + const EVP_CIPHER *cipher; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + switch (alg) { +#ifndef OPENSSL_NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + cipher = EVP_rc4(); + break; +#endif /* OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + os_free(ctx); + return NULL; + } + break; +#endif /* OPENSSL_NO_AES */ +#ifndef OPENSSL_NO_DES + case CRYPTO_CIPHER_ALG_3DES: + cipher = EVP_des_ede3_cbc(); + break; + case CRYPTO_CIPHER_ALG_DES: + cipher = EVP_des_cbc(); + break; +#endif /* OPENSSL_NO_DES */ +#ifndef OPENSSL_NO_RC2 + case CRYPTO_CIPHER_ALG_RC2: + cipher = EVP_rc2_ecb(); + break; +#endif /* OPENSSL_NO_RC2 */ + default: + os_free(ctx); + return NULL; + } + + EVP_CIPHER_CTX_init(&ctx->enc); + EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); + if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || + !EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, key, iv)) { + EVP_CIPHER_CTX_cleanup(&ctx->enc); + os_free(ctx); + return NULL; + } + + EVP_CIPHER_CTX_init(&ctx->dec); + EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); + if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || + !EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, key, iv)) { + EVP_CIPHER_CTX_cleanup(&ctx->enc); + EVP_CIPHER_CTX_cleanup(&ctx->dec); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + int outl; + if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) + return -1; + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + int outl; + outl = len; + if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) + return -1; + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + EVP_CIPHER_CTX_cleanup(&ctx->enc); + EVP_CIPHER_CTX_cleanup(&ctx->dec); + os_free(ctx); +} diff --git a/contrib/hostapd/des.c b/src/crypto/des.c index 8e0d56fc6af1d..103e592461a78 100644 --- a/contrib/hostapd/des.c +++ b/src/crypto/des.c @@ -426,6 +426,9 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) desfunc(work, ek); WPA_PUT_BE32(cypher, work[0]); WPA_PUT_BE32(cypher + 4, work[1]); + + os_memset(pkey, 0, sizeof(pkey)); + os_memset(ek, 0, sizeof(ek)); } diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c new file mode 100644 index 0000000000000..e3516324eec92 --- /dev/null +++ b/src/crypto/dh_groups.c @@ -0,0 +1,620 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "dh_groups.h" + + +/* RFC 4306, B.1. Group 1 - 768 Bit MODP + * Generator: 2 + * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } + */ +static const u8 dh_group1_generator[1] = { 0x02 }; +static const u8 dh_group1_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 4306, B.2. Group 2 - 1024 Bit MODP + * Generator: 2 + * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } + */ +static const u8 dh_group2_generator[1] = { 0x02 }; +static const u8 dh_group2_prime[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 2. Group 5 - 1536 Bit MODP + * Generator: 2 + * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } + */ +static const u8 dh_group5_generator[1] = { 0x02 }; +static const u8 dh_group5_prime[192] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 3. Group 14 - 2048 Bit MODP + * Generator: 2 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + */ +static const u8 dh_group14_generator[1] = { 0x02 }; +static const u8 dh_group14_prime[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 4. Group 15 - 3072 Bit MODP + * Generator: 2 + * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } + */ +static const u8 dh_group15_generator[1] = { 0x02 }; +static const u8 dh_group15_prime[384] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 5. Group 16 - 4096 Bit MODP + * Generator: 2 + * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } + */ +static const u8 dh_group16_generator[1] = { 0x02 }; +static const u8 dh_group16_prime[512] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 6. Group 17 - 6144 Bit MODP + * Generator: 2 + * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } + */ +static const u8 dh_group17_generator[1] = { 0x02 }; +static const u8 dh_group17_prime[768] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 7. Group 18 - 8192 Bit MODP + * Generator: 2 + * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } + */ +static const u8 dh_group18_generator[1] = { 0x02 }; +static const u8 dh_group18_prime[1024] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59, + 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4, + 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, + 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA, + 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00, + 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, + 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66, + 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68, + 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, + 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D, + 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9, + 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, + 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7, + 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B, + 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, + 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8, + 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A, + 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, + 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D, + 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36, + 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, + 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D, + 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1, + 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, + 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68, + 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92, + 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, + 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B, + 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47, + 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, + 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF, + 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71, + 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + + +#define DH_GROUP(id) \ +{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } + + +static struct dh_group dh_groups[] = { + DH_GROUP(1), + DH_GROUP(2), + DH_GROUP(5), + DH_GROUP(14), + DH_GROUP(15), + DH_GROUP(16), + DH_GROUP(17), + DH_GROUP(18) +}; + +#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) + + +const struct dh_group * dh_groups_get(int id) +{ + size_t i; + + for (i = 0; i < NUM_DH_GROUPS; i++) { + if (dh_groups[i].id == id) + return &dh_groups[i]; + } + return NULL; +} + + +/** + * dh_init - Initialize Diffie-Hellman handshake + * @dh: Selected Diffie-Hellman group + * @priv: Pointer for returning Diffie-Hellman private key + * Returns: Diffie-Hellman public value + */ +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) +{ + struct wpabuf *pv; + size_t pv_len; + + if (dh == NULL) + return NULL; + + wpabuf_free(*priv); + *priv = wpabuf_alloc(dh->prime_len); + if (*priv == NULL) + return NULL; + + if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { + wpabuf_free(*priv); + *priv = NULL; + return NULL; + } + + if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + *(wpabuf_mhead_u8(*priv)) = 0; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); + + pv_len = dh->prime_len; + pv = wpabuf_alloc(pv_len); + if (pv == NULL) + return NULL; + if (crypto_mod_exp(dh->generator, dh->generator_len, + wpabuf_head(*priv), wpabuf_len(*priv), + dh->prime, dh->prime_len, wpabuf_mhead(pv), + &pv_len) < 0) { + wpabuf_free(pv); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpabuf_put(pv, pv_len); + wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); + + return pv; +} + + +/** + * dh_derive_shared - Derive shared Diffie-Hellman key + * @peer_public: Diffie-Hellman public value from peer + * @own_private: Diffie-Hellman private key from dh_init() + * @dh: Selected Diffie-Hellman group + * Returns: Diffie-Hellman shared key + */ +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh) +{ + struct wpabuf *shared; + size_t shared_len; + + if (dh == NULL || peer_public == NULL || own_private == NULL) + return NULL; + + shared_len = dh->prime_len; + shared = wpabuf_alloc(shared_len); + if (shared == NULL) + return NULL; + if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), + wpabuf_head(own_private), wpabuf_len(own_private), + dh->prime, dh->prime_len, + wpabuf_put(shared, shared_len), &shared_len) < 0) { + wpabuf_free(shared); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); + + return shared; +} diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h new file mode 100644 index 0000000000000..5c61539b70029 --- /dev/null +++ b/src/crypto/dh_groups.h @@ -0,0 +1,32 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DH_GROUPS_H +#define DH_GROUPS_H + +struct dh_group { + int id; + const u8 *generator; + size_t generator_len; + const u8 *prime; + size_t prime_len; +}; + +const struct dh_group * dh_groups_get(int id); +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv); +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh); + +#endif /* DH_GROUPS_H */ diff --git a/contrib/hostapd/md4.c b/src/crypto/md4.c index 41c84a3a7b5d0..41c84a3a7b5d0 100644 --- a/contrib/hostapd/md4.c +++ b/src/crypto/md4.c diff --git a/contrib/hostapd/md5.c b/src/crypto/md5.c index a7db7aa9a771b..a7db7aa9a771b 100644 --- a/contrib/hostapd/md5.c +++ b/src/crypto/md5.c diff --git a/contrib/hostapd/md5.h b/src/crypto/md5.h index e82f3969ed085..e82f3969ed085 100644 --- a/contrib/hostapd/md5.h +++ b/src/crypto/md5.h diff --git a/contrib/hostapd/ms_funcs.c b/src/crypto/ms_funcs.c index d7231799d1c7b..c14af64af07d0 100644 --- a/contrib/hostapd/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -62,7 +62,7 @@ void nt_password_hash(const u8 *password, size_t password_len, size_t i, len; if (password_len > 256) - return; + password_len = 256; /* Convert password into unicode */ for (i = 0; i < password_len; i++) { @@ -110,7 +110,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, /** * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) - * @peer_hallenge: 16-octet PeerChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password: 0-to-256-unicode-char Password (IN; ASCII) @@ -135,7 +135,7 @@ void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, /** * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) - * @peer_hallenge: 16-octet PeerChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password_hash: 16-octet PasswordHash (IN) @@ -164,7 +164,7 @@ void generate_nt_response_pwhash(const u8 *auth_challenge, * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually - * encoded as a 42-octet ASCII string (S=<hexdump of response>) + * encoded as a 42-octet ASCII string (S=hexdump_of_response) */ void generate_authenticator_response_pwhash( const u8 *password_hash, @@ -219,7 +219,7 @@ void generate_authenticator_response_pwhash( * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually - * encoded as a 42-octet ASCII string (S=<hexdump of response>) + * encoded as a 42-octet ASCII string (S=hexdump_of_response) */ void generate_authenticator_response(const u8 *password, size_t password_len, const u8 *peer_challenge, @@ -355,8 +355,9 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) + * Returns: 0 on success, -1 on failure */ -static void encrypt_pw_block_with_password_hash( +int encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block) { @@ -364,11 +365,12 @@ static void encrypt_pw_block_with_password_hash( u8 *pos; if (password_len > 256) - return; + return -1; os_memset(pw_block, 0, PWBLOCK_LEN); offset = (256 - password_len) * 2; - os_get_random(pw_block, offset); + if (os_get_random(pw_block, offset) < 0) + return -1; for (i = 0; i < password_len; i++) pw_block[offset + i * 2] = password[i]; /* @@ -378,6 +380,7 @@ static void encrypt_pw_block_with_password_hash( pos = &pw_block[2 * 256]; WPA_PUT_LE16(pos, password_len * 2); rc4(pw_block, PWBLOCK_LEN, password_hash, 16); + return 0; } @@ -388,8 +391,9 @@ static void encrypt_pw_block_with_password_hash( * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + * Returns: 0 on success, -1 on failure */ -void new_password_encrypted_with_old_nt_password_hash( +int new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block) @@ -397,8 +401,11 @@ void new_password_encrypted_with_old_nt_password_hash( 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); + if (encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, + encrypted_pw_block)) + return -1; + return 0; } @@ -408,9 +415,8 @@ void new_password_encrypted_with_old_nt_password_hash( * @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) +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); @@ -423,7 +429,7 @@ static void nt_password_hash_encrypted_with_block(const u8 *password_hash, * @new_password_len: Length of new_password * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) * @old_password_len: Length of old_password - * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) + * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) */ void old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, diff --git a/contrib/hostapd/ms_funcs.h b/src/crypto/ms_funcs.h index 8067c097214aa..6205bf68d3ec9 100644 --- a/contrib/hostapd/ms_funcs.h +++ b/src/crypto/ms_funcs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -47,10 +47,15 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, 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( +int __must_check encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block); +int __must_check new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block); +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); 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, diff --git a/contrib/hostapd/rc4.c b/src/crypto/rc4.c index 8480cc55cea68..8480cc55cea68 100644 --- a/contrib/hostapd/rc4.c +++ b/src/crypto/rc4.c diff --git a/contrib/hostapd/rc4.h b/src/crypto/rc4.h index 01f13833ddc89..01f13833ddc89 100644 --- a/contrib/hostapd/rc4.h +++ b/src/crypto/rc4.h diff --git a/contrib/hostapd/sha1.c b/src/crypto/sha1.c index 194db1601d1e6..ceec1a49a6db8 100644 --- a/contrib/hostapd/sha1.c +++ b/src/crypto/sha1.c @@ -123,31 +123,29 @@ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, 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) { - u8 zero = 0, counter = 0; + u8 counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; - size_t label_len = os_strlen(label); - const unsigned char *addr[4]; - size_t len[4]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; addr[0] = (u8 *) label; len[0] = label_len; - addr[1] = &zero; - len[1] = 1; - addr[2] = data; - len[2] = data_len; - addr[3] = &counter; - len[3] = 1; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; pos = 0; while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 4, addr, len, + hmac_sha1_vector(key, key_len, 3, addr, len, &buf[pos]); pos += SHA1_MAC_LEN; } else { - hmac_sha1_vector(key, key_len, 4, addr, len, + hmac_sha1_vector(key, key_len, 3, addr, len, hash); os_memcpy(&buf[pos], hash, plen); break; @@ -157,6 +155,7 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, } +#ifndef CONFIG_NO_T_PRF /** * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) * @key: Key for PRF @@ -168,8 +167,7 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, * @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. + * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5. */ 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) @@ -210,8 +208,10 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, len[0] = SHA1_MAC_LEN; } } +#endif /* CONFIG_NO_T_PRF */ +#ifndef CONFIG_NO_TLS_PRF /** * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) * @secret: Key for PRF @@ -265,6 +265,10 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, L_S1 = L_S2 = (secret_len + 1) / 2; S1 = secret; S2 = secret + L_S1; + if (secret_len & 1) { + /* The last byte of S1 will be shared with S2 */ + S2--; + } hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); @@ -292,8 +296,11 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, return 0; } +#endif /* CONFIG_NO_TLS_PRF */ +#ifndef CONFIG_NO_PBKDF2 + static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, unsigned int count, u8 *digest) @@ -338,7 +345,7 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, * @passphrase: ASCII passphrase * @ssid: SSID * @ssid_len: SSID length in bytes - * @interations: Number of iterations to run + * @iterations: Number of iterations to run * @buf: Buffer for the generated key * @buflen: Length of the buffer in bytes * @@ -365,6 +372,8 @@ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, } } +#endif /* CONFIG_NO_PBKDF2 */ + #ifdef INTERNAL_SHA1 @@ -404,6 +413,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, } +#ifndef CONFIG_NO_FIPS186_2_PRF int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { u8 xkey[64]; @@ -456,6 +466,7 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) return 0; } +#endif /* CONFIG_NO_FIPS186_2_PRF */ /* ===== start - public domain SHA1 implementation ===== */ diff --git a/contrib/hostapd/sha1.h b/src/crypto/sha1.h index 97affa1a620db..9c365e2677bb3 100644 --- a/contrib/hostapd/sha1.h +++ b/src/crypto/sha1.h @@ -25,8 +25,9 @@ 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); 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); -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); +int __must_check 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); diff --git a/contrib/hostapd/sha256.c b/src/crypto/sha256.c index 175ec8b637da0..3d3958f91910c 100644 --- a/contrib/hostapd/sha256.c +++ b/src/crypto/sha256.c @@ -1,6 +1,6 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -61,8 +61,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * and text is the data being protected */ /* start out by storing key in ipad */ - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); /* XOR key with ipad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x36; @@ -76,8 +76,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, } sha256_vector(1 + num_elem, _addr, _len, mac); - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); /* XOR key with opad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x5c; @@ -107,7 +107,7 @@ void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, /** - * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5A.3) + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) * @key: Key for PRF * @key_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF @@ -125,28 +125,31 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, u16 counter = 0; size_t pos, plen; u8 hash[SHA256_MAC_LEN]; - const u8 *addr[3]; - size_t len[3]; - u8 counter_le[2]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; addr[0] = counter_le; len[0] = 2; addr[1] = (u8 *) label; - len[1] = strlen(label) + 1; + len[1] = os_strlen(label); addr[2] = data; len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + WPA_PUT_LE16(length_le, buf_len * 8); pos = 0; while (pos < buf_len) { plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 3, addr, len, + hmac_sha256_vector(key, key_len, 4, addr, len, &buf[pos]); pos += SHA256_MAC_LEN; } else { - hmac_sha256_vector(key, key_len, 3, addr, len, hash); - memcpy(&buf[pos], hash, plen); + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); break; } counter++; @@ -310,7 +313,7 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in, inlen -= block_size; } else { n = MIN(inlen, (block_size - md->curlen)); - memcpy(md->buf + md->curlen, in, n); + os_memcpy(md->buf + md->curlen, in, n); md->curlen += n; in += n; inlen -= n; diff --git a/contrib/hostapd/sha256.h b/src/crypto/sha256.h index dc597f09b53ae..dc597f09b53ae 100644 --- a/contrib/hostapd/sha256.h +++ b/src/crypto/sha256.h diff --git a/contrib/hostapd/tls.h b/src/crypto/tls.h index 30d8842837fc0..dafe8bb44bbeb 100644 --- a/contrib/hostapd/tls.h +++ b/src/crypto/tls.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface definition - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -63,7 +63,10 @@ struct tls_config { * @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) + * @key_id: the private key's id when using engine (this is OpenSSL + * specific for now) + * @cert_id: the certificate's id when using engine + * @ca_cert_id: the CA certificate's id when using engine * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) * * TLS connection parameters to be configured with tls_connection_set_params() @@ -98,6 +101,8 @@ struct tls_connection_params { const char *engine_id; const char *pin; const char *key_id; + const char *cert_id; + const char *ca_cert_id; }; @@ -189,8 +194,9 @@ enum { * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the * PKCS#11 engine private key. */ -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params); +int __must_check +tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); /** * tls_global_set_params - Set TLS parameters for all TLS connection @@ -202,8 +208,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the * PKCS#11 engine private key. */ -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params); +int __must_check tls_global_set_params( + void *tls_ctx, const struct tls_connection_params *params); /** * tls_global_set_verify - Set global certificate verification options @@ -212,7 +218,7 @@ int tls_global_set_params(void *tls_ctx, * 2 = verify CRL for all certificates * Returns: 0 on success, -1 on failure */ -int tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); /** * tls_connection_set_verify - Set certificate verification options @@ -221,8 +227,9 @@ int tls_global_set_verify(void *tls_ctx, int check_crl); * @verify_peer: 1 = verify peer certificate * Returns: 0 on success, -1 on failure */ -int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer); +int __must_check tls_connection_set_verify(void *tls_ctx, + struct tls_connection *conn, + int verify_peer); /** * tls_connection_set_ia - Set TLS/IA parameters @@ -234,8 +241,9 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, * This function is used to configure TLS/IA in server mode where * tls_connection_set_params() is not used. */ -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia); +int __must_check tls_connection_set_ia(void *tls_ctx, + struct tls_connection *conn, + int tls_ia); /** * tls_connection_get_keys - Get master key and random data from TLS connection @@ -244,8 +252,9 @@ int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, * @keys: Structure of key/random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys); +int __must_check tls_connection_get_keys(void *tls_ctx, + struct tls_connection *conn, + struct tls_keys *keys); /** * tls_connection_prf - Use TLS-PRF to derive keying material @@ -267,9 +276,11 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, * when it is called with seed set to client_random|server_random (or * server_random|client_random). */ -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len); +int __must_check tls_connection_prf(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + u8 *out, size_t out_len); /** * tls_connection_handshake - Process TLS handshake (client side) @@ -335,9 +346,10 @@ u8 * tls_connection_server_handshake(void *tls_ctx, * 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, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +int __must_check tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); /** * tls_connection_decrypt - Decrypt data from TLS tunnel @@ -352,9 +364,10 @@ int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, * 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, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +int __must_check tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); /** * tls_connection_resumed - Was session resumption used @@ -364,17 +377,6 @@ 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_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 - * Returns: 0 on success, -1 on failure - */ -int tls_connection_set_master_key(void *tls_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len); - enum { TLS_CIPHER_NONE, TLS_CIPHER_RC4_SHA /* 0x0005 */, @@ -391,8 +393,9 @@ enum { * (TLS_CIPHER_*). * Returns: 0 on success, -1 on failure */ -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers); +int __must_check tls_connection_set_cipher_list(void *tls_ctx, + struct tls_connection *conn, + u8 *ciphers); /** * tls_get_cipher - Get current cipher name @@ -404,8 +407,8 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, * * Get the name of the currently used cipher. */ -int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, - char *buf, size_t buflen); +int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); /** * tls_connection_enable_workaround - Enable TLS workaround options @@ -416,8 +419,8 @@ int tls_get_cipher(void *tls_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 *tls_ctx, - struct tls_connection *conn); +int __must_check tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn); /** * tls_connection_client_hello_ext - Set TLS extension for ClientHello @@ -428,9 +431,10 @@ int tls_connection_enable_workaround(void *tls_ctx, * @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); +int __must_check tls_connection_client_hello_ext(void *tls_ctx, + struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); /** * tls_connection_get_failed - Get connection failure status @@ -490,10 +494,9 @@ unsigned int tls_capabilities(void *tls_ctx); * This function is used to send the TLS/IA end phase message, e.g., when the * EAP server completes EAP-TTLSv1. */ -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len); +int __must_check tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final, + u8 *out_data, size_t out_len); /** * tls_connection_ia_final_phase_finished - Has final phase been completed @@ -502,8 +505,8 @@ int tls_connection_ia_send_phase_finished(void *tls_ctx, * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1 * on failure */ -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn); +int __must_check tls_connection_ia_final_phase_finished( + void *tls_ctx, struct tls_connection *conn); /** * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret @@ -514,8 +517,16 @@ int tls_connection_ia_final_phase_finished(void *tls_ctx, * @key_len: Length of session key material * Returns: 0 on success, -1 on failure */ -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len); +int __must_check tls_connection_ia_permute_inner_secret( + void *tls_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len); + +typedef int (*tls_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +int __must_check tls_connection_set_session_ticket_cb( + void *tls_ctx, struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx); #endif /* TLS_H */ diff --git a/contrib/hostapd/tls_gnutls.c b/src/crypto/tls_gnutls.c index 07893983189db..db66ae15e5841 100644 --- a/contrib/hostapd/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -254,8 +254,9 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *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); + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", + __func__, + (unsigned long) (end - conn->pull_buf_offset)); } return len; } @@ -943,8 +944,9 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, 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); + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) conn->pull_buf_len); os_free(conn->pull_buf); } conn->pull_buf = os_malloc(in_len); @@ -1058,6 +1060,14 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, return -1; if (conn->push_buf_len < out_len) out_len = conn->push_buf_len; + else if (conn->push_buf_len > out_len) { + wpa_printf(MSG_INFO, "GnuTLS: Not enough buffer space for " + "encrypted message (in_len=%lu push_buf_len=%lu " + "out_len=%lu", + (unsigned long) in_len, + (unsigned long) conn->push_buf_len, + (unsigned long) out_len); + } os_memcpy(out_data, conn->push_buf, out_len); os_free(conn->push_buf); conn->push_buf = NULL; @@ -1073,8 +1083,9 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, ssize_t res; if (conn->pull_buf) { - wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " - "pull_buf", __func__, conn->pull_buf_len); + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) conn->pull_buf_len); os_free(conn->pull_buf); } conn->pull_buf = os_malloc(in_len); @@ -1132,7 +1143,7 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, if (res < 0) { wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, res, + "(%s)", __func__, (int) res, gnutls_strerror(res)); } return res; @@ -1142,7 +1153,7 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, 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)); + "(%s)", __func__, (int) res, gnutls_strerror(res)); } return res; @@ -1157,14 +1168,6 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } -int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - /* TODO */ - return -1; -} - - int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c new file mode 100644 index 0000000000000..42120c8a882e0 --- /dev/null +++ b/src/crypto/tls_internal.c @@ -0,0 +1,569 @@ +/* + * WPA Supplicant / TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file interface functions for hostapd/wpa_supplicant to use the + * integrated TLSv1 implementation. + */ + +#include "includes.h" + +#include "common.h" +#include "tls.h" +#include "tls/tlsv1_client.h" +#include "tls/tlsv1_server.h" + + +static int tls_ref_count = 0; + +struct tls_global { + int server; + struct tlsv1_credentials *server_cred; + int check_crl; +}; + +struct tls_connection { + struct tlsv1_client *client; + struct tlsv1_server *server; +}; + + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + + if (tls_ref_count == 0) { +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (tlsv1_client_global_init()) + return NULL; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (tlsv1_server_global_init()) + return NULL; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + } + tls_ref_count++; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + return global; +} + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + tls_ref_count--; + if (tls_ref_count == 0) { +#ifdef CONFIG_TLS_INTERNAL_CLIENT + tlsv1_client_global_deinit(); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); + tlsv1_server_global_deinit(); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + } + os_free(global); +} + + +int tls_get_errors(void *tls_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + struct tls_connection *conn; + struct tls_global *global = tls_ctx; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (!global->server) { + conn->client = tlsv1_client_init(); + if (conn->client == NULL) { + os_free(conn); + return NULL; + } + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (global->server) { + conn->server = tlsv1_server_init(global->server_cred); + if (conn->server == NULL) { + os_free(conn); + return NULL; + } + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + tlsv1_client_deinit(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + tlsv1_server_deinit(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_established(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_established(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return 0; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_shutdown(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_shutdown(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + struct tlsv1_credentials *cred; + + if (conn->client == NULL) + return -1; + + cred = tlsv1_cred_alloc(); + if (cred == NULL) + return -1; + + if (tlsv1_set_ca_cert(cred, params->ca_cert, + params->ca_cert_blob, params->ca_cert_blob_len, + params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure client " + "certificate"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_client_set_cred(conn->client, cred) < 0) { + tlsv1_cred_free(cred); + return -1; + } + + return 0; +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + struct tls_global *global = tls_ctx; + struct tlsv1_credentials *cred; + + /* Currently, global parameters are only set when running in server + * mode. */ + global->server = 1; + tlsv1_cred_free(global->server_cred); + global->server_cred = cred = tlsv1_cred_alloc(); + if (cred == NULL) + return -1; + + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, + params->ca_cert_blob_len, params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure server " + "certificate"); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + return -1; + } + + return 0; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + struct tls_global *global = tls_ctx; + global->check_crl = check_crl; + return 0; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_verify(conn->server, verify_peer); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, + int tls_ia) +{ + return -1; +} + + +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_keys(conn->client, keys); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_keys(conn->server, keys); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_prf(conn->client, label, + server_random_first, + out, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + return tlsv1_server_prf(conn->server, label, + server_random_first, + out, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client == NULL) + return NULL; + + if (appl_data) + *appl_data = NULL; + + wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", + __func__, in_data, (unsigned long) in_len); + return tlsv1_client_handshake(conn->client, in_data, in_len, out_len, + appl_data, appl_data_len); +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +u8 * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + u8 *out; + if (conn->server == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", + __func__, in_data, (unsigned long) in_len); + out = tlsv1_server_handshake(conn->server, in_data, in_len, out_len); + if (out == NULL && tlsv1_server_established(conn->server)) { + out = os_malloc(1); + *out_len = 0; + } + return out; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_encrypt(conn->client, in_data, in_len, + out_data, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + return tlsv1_server_encrypt(conn->server, in_data, in_len, + out_data, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_decrypt(conn->client, in_data, in_len, + out_data, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + return tlsv1_server_decrypt(conn->server, in_data, in_len, + out_data, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_resumed(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_resumed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_set_cipher_list(conn->client, ciphers); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_cipher_list(conn->server, ciphers); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_cipher(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_cipher(conn->server, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_hello_ext(conn->client, ext_type, + data, data_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_keyblock_size(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_keyblock_size(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +int tls_connection_ia_send_phase_finished(void *tls_ctx, + struct tls_connection *conn, + int final, + u8 *out_data, size_t out_len) +{ + return -1; +} + + +int tls_connection_ia_final_phase_finished(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_ia_permute_inner_secret(void *tls_ctx, + struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + return -1; +} + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx); + return 0; + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx); + return 0; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} diff --git a/contrib/hostapd/tls_none.c b/src/crypto/tls_none.c index ad08d5076c880..f731628b0e380 100644 --- a/contrib/hostapd/tls_none.c +++ b/src/crypto/tls_none.c @@ -147,13 +147,6 @@ int tls_connection_resumed(void *tls_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) -{ - return -1; -} - - int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { diff --git a/contrib/hostapd/tls_openssl.c b/src/crypto/tls_openssl.c index d5aafaaa89d49..f290a39c72dc5 100644 --- a/contrib/hostapd/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -37,6 +37,16 @@ #define OPENSSL_d2i_TYPE unsigned char ** #endif +#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT +#ifdef SSL_OP_NO_TICKET +/* + * Session ticket override patch was merged into OpenSSL 0.9.9 tree on + * 2008-11-15. This version uses a bit different API compared to the old patch. + */ +#define CONFIG_OPENSSL_TICKET_OVERRIDE +#endif +#endif + static int tls_openssl_ref_count = 0; struct tls_connection { @@ -49,8 +59,12 @@ struct tls_connection { char *subject_match, *altsubject_match; int read_alerts, write_alerts, failed; - u8 *pre_shared_secret; - size_t pre_shared_secret_len; + tls_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + /* SessionTicket received from OpenSSL hello_extension_cb (server) */ + u8 *session_ticket; + size_t session_ticket_len; }; @@ -106,10 +120,12 @@ static BOOL WINAPI DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) = NULL; /* to be loaded from crypt32.dll */ +#ifdef CONFIG_MINGW32_LOAD_CERTENUM static PCCERT_CONTEXT WINAPI (*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext) = NULL; /* to be loaded from crypt32.dll */ +#endif /* CONFIG_MINGW32_LOAD_CERTENUM */ static int mingw_load_crypto_func(void) { @@ -137,6 +153,7 @@ static int mingw_load_crypto_func(void) return -1; } +#ifdef CONFIG_MINGW32_LOAD_CERTENUM CertEnumCertificatesInStore = (void *) GetProcAddress( dll, "CertEnumCertificatesInStore"); if (CertEnumCertificatesInStore == NULL) { @@ -145,6 +162,7 @@ static int mingw_load_crypto_func(void) "crypt32 library"); return -1; } +#endif /* CONFIG_MINGW32_LOAD_CERTENUM */ return 0; } @@ -764,6 +782,8 @@ void tls_deinit(void *ssl_ctx) #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); } @@ -771,7 +791,8 @@ void tls_deinit(void *ssl_ctx) static int tls_engine_init(struct tls_connection *conn, const char *engine_id, - const char *pin, const char *key_id) + const char *pin, const char *key_id, + const char *cert_id, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE int ret = -1; @@ -808,6 +829,7 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, ERR_error_string(ERR_get_error(), NULL)); goto err; } + /* load private key first in-case PIN is required for cert */ conn->private_key = ENGINE_load_private_key(conn->engine, key_id, NULL, NULL); if (!conn->private_key) { @@ -817,6 +839,21 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } + + /* handle a certificate and/or CA certificate */ + if (cert_id || ca_cert_id) { + const char *cmd_name = "LOAD_CERT_CTRL"; + + /* test if the engine supports a LOAD_CERT_CTRL */ + if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME, + 0, (void *)cmd_name, NULL)) { + wpa_printf(MSG_ERROR, "ENGINE: engine does not support" + " loading certificates"); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + } + return 0; err: @@ -871,6 +908,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; + long options; conn = os_zalloc(sizeof(*conn)); if (conn == NULL) @@ -884,9 +922,12 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) } 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); + options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_SINGLE_DH_USE; +#ifdef SSL_OP_NO_COMPRESSION + options |= SSL_OP_NO_COMPRESSION; +#endif /* SSL_OP_NO_COMPRESSION */ + SSL_set_options(conn->ssl, options); conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { @@ -917,11 +958,11 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; - os_free(conn->pre_shared_secret); SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->session_ticket); os_free(conn); } @@ -1255,6 +1296,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int verify_peer) { + static int counter = 0; + if (conn == NULL) return -1; @@ -1268,6 +1311,19 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_accept_state(conn->ssl); + /* + * Set session id context in order to avoid fatal errors when client + * tries to resume a session. However, set the context to a unique + * value in order to effectively disable session resumption for now + * since not all areas of the server code are ready for it (e.g., + * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS + * handshake). + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + return 0; } @@ -1351,8 +1407,7 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) if (password == NULL) { return 0; } - os_strncpy(buf, (char *) password, size); - buf[size - 1] = '\0'; + os_strlcpy(buf, (char *) password, size); return os_strlen(buf); } @@ -1486,6 +1541,110 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, } +#ifndef OPENSSL_NO_ENGINE +static int tls_engine_get_cert(struct tls_connection *conn, + const char *cert_id, + X509 **cert) +{ + /* this runs after the private key is loaded so no PIN is required */ + struct { + const char *cert_id; + X509 *cert; + } params; + params.cert_id = cert_id; + params.cert = NULL; + + if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", + 0, ¶ms, NULL, 1)) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" + " '%s' [%s]", cert_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + if (!params.cert) { + wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id" + " '%s'", cert_id); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + *cert = params.cert; + return 0; +} +#endif /* OPENSSL_NO_ENGINE */ + + +static int tls_connection_engine_client_cert(struct tls_connection *conn, + const char *cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + + if (tls_engine_get_cert(conn, cert_id, &cert)) + return -1; + + if (!SSL_use_certificate(conn->ssl, cert)) { + tls_show_errors(MSG_ERROR, __func__, + "SSL_use_certificate failed"); + X509_free(cert); + return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> " + "OK"); + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_engine_ca_cert(void *_ssl_ctx, + struct tls_connection *conn, + const char *ca_cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (tls_engine_get_cert(conn, ca_cert_id, &cert)) + return -1; + + /* start off the same as tls_connection_ca_cert */ + X509_STORE_free(ssl_ctx->cert_store); + ssl_ctx->cert_store = X509_STORE_new(); + if (ssl_ctx->cert_store == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " + "certificate store", __func__); + X509_free(cert); + return -1; + } + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed to add CA certificate from engine " + "to certificate store"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert" + " already in hash table error", + __func__); + } else { + X509_free(cert); + return -1; + } + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + static int tls_connection_engine_private_key(struct tls_connection *conn) { #ifndef OPENSSL_NO_ENGINE @@ -1771,6 +1930,80 @@ static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) } +static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +{ +#ifdef OPENSSL_NO_DH + if (dh_file == NULL) + return 0; + wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " + "dh_file specified"); + return -1; +#else /* OPENSSL_NO_DH */ + DH *dh; + BIO *bio; + + /* TODO: add support for dh_blob */ + if (dh_file == NULL) + return 0; + if (ssl_ctx == NULL) + return -1; + + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", + dh_file, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); +#ifndef OPENSSL_NO_DSA + while (dh == NULL) { + DSA *dsa; + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" + " trying to parse as DSA params", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) + break; + dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dsa) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " + "'%s': %s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " + "params into DH params"); + break; + } + break; + } +#endif /* !OPENSSL_NO_DSA */ + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " + "'%s'", dh_file); + return -1; + } + + if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " + "%s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return -1; + } + DH_free(dh); + return 0; +#endif /* OPENSSL_NO_DH */ +} + + int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -1897,8 +2130,11 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, { int res; u8 *out_data; - char buf[10]; + /* + * Give TLS handshake data from the client (if available) to OpenSSL + * for processing. + */ if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { tls_show_errors(MSG_INFO, __func__, @@ -1906,12 +2142,23 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, return NULL; } - res = SSL_read(conn->ssl, buf, sizeof(buf)); - if (res >= 0) { - wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read " - "(res=%d)", res); + /* Initiate TLS handshake or continue the existing handshake */ + res = SSL_accept(conn->ssl); + if (res != 1) { + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ) + wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want " + "more data"); + else if (err == SSL_ERROR_WANT_WRITE) + wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want to " + "write"); + else { + tls_show_errors(MSG_INFO, __func__, "SSL_accept"); + return NULL; + } } + /* Get the TLS handshake data to be sent to the client */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); out_data = os_malloc(res == 0 ? 1 : res); @@ -2011,57 +2258,6 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) -/* 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; - - os_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) -{ - if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) - return -1; - - os_free(conn->pre_shared_secret); - conn->pre_shared_secret = NULL; - conn->pre_shared_secret_len = 0; - - if (key) { - conn->pre_shared_secret = os_malloc(key_len); - if (conn->pre_shared_secret) { - os_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 || EAP_FAST_DYNAMIC */ - - int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { @@ -2129,8 +2325,7 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, if (name == NULL) return -1; - os_snprintf(buf, buflen, "%s", name); - buf[buflen - 1] = '\0'; + os_strlcpy(buf, name, buflen); return 0; } @@ -2152,12 +2347,18 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { - if (conn == NULL || conn->ssl == NULL) + if (conn == NULL || conn->ssl == NULL || ext_type != 35) return -1; +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, + data_len) != 1) + return -1; +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, data_len) != 1) return -1; +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ return 0; } @@ -2202,26 +2403,39 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, __func__, ERR_error_string(err, NULL)); } + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id, params->cert_id, + params->ca_cert_id); + if (ret) + return ret; + } if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match)) 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)) + + if (params->engine && params->ca_cert_id) { + if (tls_connection_engine_ca_cert(tls_ctx, conn, + params->ca_cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) return -1; - if (tls_connection_client_cert(conn, params->client_cert, - params->client_cert_blob, - params->client_cert_blob_len)) + + if (params->engine && params->cert_id) { + if (tls_connection_engine_client_cert(conn, params->cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) return -1; - if (params->engine) { - 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 (params->engine && params->key_id) { + wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_private_key(tls_ctx, conn, @@ -2267,6 +2481,12 @@ int tls_global_set_params(void *tls_ctx, params->private_key_passwd)) return -1; + if (tls_global_dh(ssl_ctx, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + return 0; } @@ -2331,3 +2551,168 @@ int tls_connection_ia_permute_inner_secret(void *tls_ctx, { return -1; } + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +/* Pre-shared secred requires a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + int ret; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + s->s3->client_random, + s->s3->server_random, secret); + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + if (ret <= 0) + return 0; + + *secret_len = SSL_MAX_MASTER_KEY_LENGTH; + return 1; +} + + +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE +static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, + int len, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", data, len); + + conn->session_ticket = os_malloc(len); + if (conn->session_ticket == NULL) + return 0; + + os_memcpy(conn->session_ticket, data, len); + conn->session_ticket_len = len; + + return 1; +} +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET +static void tls_hello_ext_cb(SSL *s, int client_server, int type, + unsigned char *data, int len, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, + type, len); + + if (type == TLSEXT_TYPE_session_ticket && !client_server) { + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", data, len); + conn->session_ticket = os_malloc(len); + if (conn->session_ticket == NULL) + return; + + os_memcpy(conn->session_ticket, data, len); + conn->session_ticket_len = len; + } +} +#else /* SSL_OP_NO_TICKET */ +static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, + ext->type, ext->length); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + if (ext->type == 35) { + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", ext->data, ext->length); + conn->session_ticket = os_malloc(ext->length); + if (conn->session_ticket == NULL) + return SSL_AD_INTERNAL_ERROR; + + os_memcpy(conn->session_ticket, ext->data, ext->length); + conn->session_ticket_len = ext->length; + } + + return 0; +} +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; + + if (cb) { + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + SSL_set_session_ticket_ext_cb(conn->ssl, + tls_session_ticket_ext_cb, conn); +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET + SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); + SSL_set_tlsext_debug_arg(conn->ssl, conn); +#else /* SSL_OP_NO_TICKET */ + if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, + conn) != 1) + return -1; +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET + SSL_set_tlsext_debug_callback(conn->ssl, NULL); + SSL_set_tlsext_debug_arg(conn->ssl, conn); +#else /* SSL_OP_NO_TICKET */ + if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) + return -1; +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + } + + return 0; +#else /* EAP_FAST || EAP_FAST_DYNAMIC */ + return -1; +#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +} diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c new file mode 100644 index 0000000000000..87e74353dcf9c --- /dev/null +++ b/src/crypto/tls_schannel.c @@ -0,0 +1,789 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for Microsoft Schannel + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/* + * FIX: Go through all SSPI functions and verify what needs to be freed + * FIX: session resumption + * TODO: add support for server cert chain validation + * TODO: add support for CA cert validation + * TODO: add support for EAP-TLS (client cert/key conf) + */ + +#include "includes.h" +#include <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(TEXT("Secur32.dll")); + if (global->hsecurity == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( + global->hsecurity, "InitSecurityInterfaceA"); + if (pInitSecurityInterface == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not find " + "InitSecurityInterfaceA from Secur32.dll", + __func__); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + global->sspi = pInitSecurityInterface(); + if (global->sspi == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not read security " + "interface - 0x%x", + __func__, (unsigned int) GetLastError()); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + return 0; +} + + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + if (schannel_load_lib(global)) { + os_free(global); + return NULL; + } + return global; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + + if (global->my_cert_store) + CertCloseStore(global->my_cert_store, 0); + FreeLibrary(global->hsecurity); + os_free(global); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_connection *conn; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + conn->start = 1; + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + + os_free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + struct tls_global *global = ssl_ctx; + if (conn == NULL) + return -1; + + conn->eap_tls_prf_set = 0; + conn->established = conn->failed = 0; + conn->read_alerts = conn->write_alerts = 0; + global->sspi->DeleteSecurityContext(&conn->context); + /* FIX: what else needs to be reseted? */ + + return 0; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + return -1; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + /* Schannel does not export master secret or client/server random. */ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + /* + * Cannot get master_key from Schannel, but EapKeyBlock can be used to + * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and + * EAP-TTLS cannot use this, though, since they are using different + * labels. The only option could be to implement TLSv1 completely here + * and just use Schannel or CryptoAPI for low-level crypto + * functionality.. + */ + + if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || + os_strcmp(label, "client EAP encryption") != 0 || + out_len > sizeof(conn->eap_tls_prf)) + return -1; + + os_memcpy(out, conn->eap_tls_prf, out_len); + + return 0; +} + + +static 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; + +#ifdef UNICODE + status = global->sspi->InitializeSecurityContextW( + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outbuf, &sspi_flags_out, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->InitializeSecurityContextA( + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outbuf, &sspi_flags_out, &ts_expiry); +#endif /* UNICODE */ + if (status != SEC_I_CONTINUE_NEEDED) { + wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " + "failed - 0x%x", + __func__, (unsigned int) status); + return NULL; + } + + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + u8 *buf; + wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + conn->start = 0; + *out_len = outbufs[0].cbBuffer; + buf = os_malloc(*out_len); + if (buf == NULL) + return NULL; + os_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)); + + os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); + conn->eap_tls_prf_set = 1; + 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 **appl_data, + size_t *appl_data_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 (appl_data) + *appl_data = 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; + +#ifdef UNICODE + status = global->sspi->InitializeSecurityContextW( + &conn->creds, &conn->context, NULL, sspi_flags, 0, + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + &outbuf, &sspi_flags_out, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->InitializeSecurityContextA( + &conn->creds, &conn->context, NULL, sspi_flags, 0, + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + &outbuf, &sspi_flags_out, &ts_expiry); +#endif /* UNICODE */ + + wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " + "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " + "intype[1]=%d outlen[0]=%d", + (int) status, (int) inbufs[0].cbBuffer, + (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, + (int) inbufs[1].BufferType, + (int) outbufs[0].cbBuffer); + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || + (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - output", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + *out_len = outbufs[0].cbBuffer; + out_buf = os_malloc(*out_len); + if (out_buf) + os_memcpy(out_buf, outbufs[0].pvBuffer, + *out_len); + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + outbufs[0].pvBuffer = NULL; + if (out_buf == NULL) + return NULL; + } + } + + switch (status) { + case SEC_E_INCOMPLETE_MESSAGE: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); + break; + case SEC_I_CONTINUE_NEEDED: + wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); + break; + case SEC_E_OK: + /* TODO: verify server certificate chain */ + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " + "completed successfully"); + conn->established = 1; + tls_get_eap(global, conn); + + /* Need to return something to get final TLS ACK. */ + if (out_buf == NULL) + out_buf = os_malloc(1); + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " + "application data", + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + if (appl_data) { + *appl_data_len = outbufs[1].cbBuffer; + appl_data = os_malloc(*appl_data_len); + if (appl_data) + os_memcpy(appl_data, + outbufs[1].pvBuffer, + *appl_data_len); + } + 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; + } + + os_memset(&bufs, 0, sizeof(bufs)); + bufs[0].pvBuffer = out_data; + bufs[0].cbBuffer = sizes.cbHeader; + bufs[0].BufferType = SECBUFFER_STREAM_HEADER; + + os_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); + os_memset(&bufs, 0, sizeof(bufs)); + os_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; + } + os_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; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + struct tls_global *global = tls_ctx; + ALG_ID algs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + + if (conn == NULL) + return -1; + + if (global->my_cert_store == NULL && + (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == + NULL) { + wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); + conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; + algs[0] = CALG_RSA_KEYX; + conn->schannel_cred.cSupportedAlgs = 1; + conn->schannel_cred.palgSupportedAlgs = algs; + conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; +#ifdef UNICODE + status = global->sspi->AcquireCredentialsHandleW( + NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->AcquireCredentialsHandleA( + NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); +#endif /* UNICODE */ + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " + "0x%x", __func__, (unsigned int) status); + return -1; + } + + return 0; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, + int tls_ia) +{ + return -1; +} + + +int tls_connection_ia_send_phase_finished(void *tls_ctx, + struct tls_connection *conn, + int final, + u8 *out_data, size_t out_len) +{ + return -1; +} + + +int tls_connection_ia_final_phase_finished(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_ia_permute_inner_secret(void *tls_ctx, + struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + return -1; +} diff --git a/src/drivers/.gitignore b/src/drivers/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/drivers/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h new file mode 100644 index 0000000000000..2a612e73083a6 --- /dev/null +++ b/src/drivers/Apple80211.h @@ -0,0 +1,156 @@ +#ifndef APPLE80211_H +#define APPLE80211_H + +/* + * Apple80211 framework definitions + * This is an undocumented interface and the definitions here are based on + * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and + * whatever related information can be found with google and experiments ;-). + */ + +typedef struct __WirelessRef *WirelessRef; +typedef SInt32 WirelessError; +#define errWirelessNoError 0 + +typedef struct WirelessInfo { + UInt16 link_qual; + UInt16 comms_qual; + UInt16 signal; + UInt16 noise; + UInt16 port_stat; + UInt16 client_mode; + UInt16 res1; + UInt16 power; + UInt16 res2; + UInt8 bssID[6]; + UInt8 ssid[34]; +} WirelessInfo; + +typedef struct WirelessInfo2 { + /* TODO - these are probably not in correct order or complete */ + WirelessInfo info1; + UInt8 macAddress[6]; +} WirelessInfo2; + +typedef struct WirelessNetworkInfo { + UInt16 channel; + UInt16 noise; + UInt16 signal; + UInt8 bssid[6]; + UInt16 beacon_int; + UInt16 capability; + UInt16 ssid_len; + UInt8 ssid[32]; +} WirelessNetworkInfo; + +typedef int wirelessKeyType; /* TODO */ + +int WirelessIsAvailable(void); +WirelessError WirelessAttach(WirelessRef *ref, UInt32 res); +WirelessError WirelessDetach(WirelessRef ref); +WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes, + void *out_ptr, int out_bytes); +WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled); +WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled); +WirelessError WirelessSetPower(WirelessRef ref, UInt8 power); +WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power); +WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info); +WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info); +WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups); +WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results, + CFArrayRef *ibss_results, UInt32 strip_dups); +WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups, CFStringRef ssid); +WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid, + UInt32 strip_dups, CFArrayRef *results); +WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid); +WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid, + CFStringRef passwd); +WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid); +/* + * Set WEP key + * ref: wireless reference from WirelessAttach() + * type: ? + * key_idx: 0..3 + * key_len: 13 for WEP-104 or 0 for clearing the key + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type, + int key_idx, int key_len, + const unsigned char *key); +/* + * Set WPA key (e.g., PMK for 4-way handshake) + * ref: wireless reference from WirelessAttach() + * type: 0..4; 1 = PMK + * key_len: 16, 32, or 0 + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type, + int key_len, const unsigned char *key); +WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid, + CFStringRef key); +WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res, + CFStringRef key); +WirelessError WirelessDisassociate(WirelessRef ref); + +/* + * Get a copy of scan results for the given SSID + * The returned dictionary includes following entries: + * beaconInterval: CFNumber(kCFNumberSInt32Type) + * SSID: CFData buffer of the SSID + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2 + * name: Name of the network (SSID string) + * BSSID: CFData buffer of the BSSID + * channel: CFNumber(kCFNumberSInt32Type) + * signal: CFNumber(kCFNumberSInt32Type) + * appleIE: CFData + * WPSNOPINRequired: CFBoolean + * noise: CFNumber(kCFNumberSInt32Type) + * capability: CFNumber(kCFNumberSInt32Type) + * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type) + * appleIE_Version: CFNumber(kCFNumberSInt32Type) + * appleIE_Robust: CFBoolean + * WPSConfigured: CFBoolean + * scanWasDirected: CFBoolean + * appleIE_Product: CFNumber(kCFNumberSInt32Type) + * authModes: CFArray of CFNumber(kCFNumberSInt32Type) + * multiCipher: CFNumber(kCFNumberSInt32Type) + */ +CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid); + +/* + * Get information about the current association + * The returned dictionary includes following entries: + * keyData: CFData buffer of the key (e.g., 32-octet PSK) + * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP? + * channel: CFNumber(kCFNumberSInt32Type) + * isIBSS: CFBoolean + * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open, + * 129 = WPA2-Enterprise + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2 + * SSID: CFData buffer of the SSID + * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP? + */ +CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref); + +WirelessError WirelessConfigure(WirelessRef ref); + +/* + * Get ASP information + * The returned dictionary includes following entries: + * Version: version number (e.g., 3.0) + * Channel: channel (e.g., 1) + * Vendor: vendor (e.g., 2) + */ +CFDictionaryRef WirelessGetInfoASP(void); + +/* + * Get a copy of the interface dictionary + * The returned dictionary has a key,value pairs for wireless interfaces. + * The key is the interface name and the value is the driver identifier, e.g., + * en1: com.apple.driver.AirPort.Atheros + */ +CFDictionaryRef WirelessCopyInterfaceDict(void); + +#endif /* APPLE80211_H */ diff --git a/src/drivers/Makefile b/src/drivers/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/drivers/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c new file mode 100644 index 0000000000000..ce004fe4c96f5 --- /dev/null +++ b/src/drivers/MobileApple80211.c @@ -0,0 +1,189 @@ +#include "includes.h" +#include <dlfcn.h> + +#include "common.h" + +#include <CoreFoundation/CoreFoundation.h> +#include "MobileApple80211.h" + +/* + * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid + * having to link with full Preferences.framework. + */ + +static void *aeropuerto = NULL; + + +int _Apple80211Initialized(void) +{ + return aeropuerto ? 1 : 0; +} + + +static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL; + +int Apple80211Open(Apple80211Ref *ctx) +{ + return __Apple80211Open(ctx); +} + + +static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL; + +int Apple80211Close(Apple80211Ref ctx) +{ + return __Apple80211Close(ctx); +} + + +static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list) + = NULL; + +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list) +{ + return __Apple80211GetIfListCopy(handle, list); +} + + +static int (*__Apple80211BindToInterface)(Apple80211Ref handle, + CFStringRef interface) = NULL; + +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface) +{ + return __Apple80211BindToInterface(handle, interface); +} + + +static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle, + CFStringRef *name) = NULL; + +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name) +{ + return __Apple80211GetInterfaceNameCopy(handle, name); +} + + +static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle, + CFDictionaryRef *info) = NULL; + +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info) +{ + return __Apple80211GetInfoCopy(handle, info); +} + + +static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL; + +int Apple80211GetPower(Apple80211Ref handle, char *pwr) +{ + return __Apple80211GetPower(handle, pwr); +} + + +static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL; + +int Apple80211SetPower(Apple80211Ref handle, char pwr) +{ + return __Apple80211SetPower(handle, pwr); +} + + +static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) = NULL; + +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) +{ + return __Apple80211Scan(handle, list, parameters); +} + + +static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) = NULL; + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) +{ + return __Apple80211Associate(handle, bss, password); +} + + +static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle, + CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info) = + NULL; + +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, CFDictionaryRef *info) +{ + return __Apple80211AssociateAndCopyInfo(handle, bss, password, info); +} + + +static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field, + CFDictionaryRef arg2, void *value) = NULL; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value) +{ + return __Apple80211CopyValue(handle, field, arg2, value); +} + + +#define DLSYM(s) \ +do { \ + __ ## s = dlsym(aeropuerto, #s); \ + if (__ ## s == NULL) { \ + wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \ + "symbol '" #s "' (%s)", dlerror()); \ + err = 1; \ + } \ +} while (0) + + +__attribute__ ((constructor)) +void _Apple80211_constructor(void) +{ + const char *fname = "/System/Library/SystemConfiguration/" + "Aeropuerto.bundle/Aeropuerto"; + int err = 0; + + aeropuerto = dlopen(fname, RTLD_LAZY); + if (!aeropuerto) { + wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s " + "for symbols", fname); + return; + } + + DLSYM(Apple80211Open); + DLSYM(Apple80211Close); + DLSYM(Apple80211GetIfListCopy); + DLSYM(Apple80211BindToInterface); + DLSYM(Apple80211GetInterfaceNameCopy); + DLSYM(Apple80211GetInfoCopy); + DLSYM(Apple80211GetPower); + DLSYM(Apple80211SetPower); + DLSYM(Apple80211Scan); + DLSYM(Apple80211Associate); + DLSYM(Apple80211AssociateAndCopyInfo); + DLSYM(Apple80211CopyValue); + + if (err) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} + + +__attribute__ ((destructor)) +void _Apple80211_destructor(void) +{ + if (aeropuerto) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h new file mode 100644 index 0000000000000..64d439d660c8d --- /dev/null +++ b/src/drivers/MobileApple80211.h @@ -0,0 +1,43 @@ +#ifndef MOBILEAPPLE80211_H +#define MOBILEAPPLE80211_H + +/* + * MobileApple80211 interface for iPhone/iPod touch + * These functions are available from Aeropuerto. + */ + +struct Apple80211; +typedef struct Apple80211 *Apple80211Ref; + +int Apple80211Open(Apple80211Ref *ctx); +int Apple80211Close(Apple80211Ref ctx); +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list); +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface); +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name); +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info); +int Apple80211GetPower(Apple80211Ref handle, char *pwr); +int Apple80211SetPower(Apple80211Ref handle, char pwr); + +/* parameters can be NULL; returns scan results in CFArrayRef *list; + * caller will need to free with CFRelease() */ +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters); + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password); +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info); + +enum { + APPLE80211_VALUE_SSID = 1, + APPLE80211_VALUE_BSSID = 9 +}; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value); + +#endif /* MOBILEAPPLE80211_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h new file mode 100644 index 0000000000000..9bce6c6e8401f --- /dev/null +++ b/src/drivers/driver.h @@ -0,0 +1,1325 @@ +/* + * WPA Supplicant - driver interface definition + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#define WPA_SUPPLICANT_DRIVER_VERSION 3 + +#include "defs.h" + +#define AUTH_ALG_OPEN_SYSTEM 0x01 +#define AUTH_ALG_SHARED_KEY 0x02 +#define AUTH_ALG_LEAP 0x04 + +#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 (old structure) + * @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 + * @mdie_present: Whether MDIE was included in Beacon/ProbeRsp frame + * @mdie: Mobility domain identifier IE (IEEE 802.11r MDIE) (starting from + * IE type field) + * @tsf: Timestamp + * + * 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. + * + * This structure is the old data structure used for scan results. It is + * obsoleted by the new struct wpa_scan_res structure and the old version is + * only included for backwards compatibility with existing driver wrapper + * implementations. New implementations are encouraged to implement for struct + * wpa_scan_res. The old structure will be removed at some point. + */ +struct wpa_scan_result { + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + int freq; + u16 caps; + int qual; + int noise; + int level; + int maxrate; + int mdie_present; + u8 mdie[5]; + u64 tsf; +}; + + +/** + * struct wpa_scan_res - Scan result for an BSS/IBSS + * @bssid: BSSID + * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) + * @beacon_int: beacon interval in TUs (host byte order) + * @caps: capability information field in host byte order + * @qual: signal quality + * @noise: noise level + * @level: signal level + * @tsf: Timestamp + * @ie_len: length of the following IE field in octets + * + * This structure is used as a generic format for scan results from the + * driver. Each driver interface implementation is responsible for converting + * the driver or OS specific scan results into this format. + * + * If the driver does not support reporting all IEs, the IE data structure is + * constructed of the IEs that are available. This field will also need to + * include SSID in IE format. All drivers are encouraged to be extended to + * report all IEs to make it easier to support future additions. + */ +struct wpa_scan_res { + u8 bssid[ETH_ALEN]; + int freq; + u16 beacon_int; + u16 caps; + int qual; + int noise; + int level; + u64 tsf; + size_t ie_len; + /* followed by ie_len octets of IEs */ +}; + +/** + * struct wpa_scan_results - Scan results + * @res: Array of pointers to allocated variable length scan result entries + * @num: Number of entries in the scan result array + */ +struct wpa_scan_results { + struct wpa_scan_res **res; + size_t num; +}; + +/** + * struct wpa_interface_info - Network interface information + * @next: Pointer to the next interface or NULL if this is the last one + * @ifname: Interface name that can be used with init() or init2() + * @desc: Human readable adapter description (e.g., vendor/model) or NULL if + * not available + * @drv_bame: struct wpa_driver_ops::name (note: unlike other strings, this one + * is not an allocated copy, i.e., get_interfaces() caller will not free + * this) + */ +struct wpa_interface_info { + struct wpa_interface_info *next; + char *ifname; + char *desc; + const char *drv_name; +}; + +/** + * struct wpa_driver_associate_params - Association parameters + * Data for struct wpa_driver_ops::associate(). + */ +struct wpa_driver_associate_params { + /** + * bssid - BSSID of the selected AP + * This can be %NULL, if ap_scan=2 mode is used and the driver is + * responsible for selecting with which BSS to associate. */ + const u8 *bssid; + + /** + * ssid - The selected SSID + */ + const u8 *ssid; + size_t ssid_len; + + /** + * freq - Frequency of the channel the selected AP is using + * Frequency that the selected AP is using (in MHz as + * reported in the scan results) + */ + int freq; + + /** + * wpa_ie - WPA information element for (Re)Association Request + * WPA information element to be included in (Re)Association + * Request (including information element id and length). Use + * of this WPA IE is optional. If the driver generates the WPA + * IE, it can use pairwise_suite, group_suite, and + * key_mgmt_suite to select proper algorithms. In this case, + * the driver has to notify wpa_supplicant about the used WPA + * IE by generating an event that the interface code will + * convert into EVENT_ASSOCINFO data (see below). + * + * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE + * instead. The driver can determine which version is used by + * looking at the first byte of the IE (0xdd for WPA, 0x30 for + * WPA2/RSN). + * + * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. + */ + const u8 *wpa_ie; + /** + * wpa_ie_len - length of the wpa_ie + */ + size_t wpa_ie_len; + + /* The selected pairwise/group cipher and key management + * suites. These are usually ignored if @wpa_ie is used. */ + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + + /** + * auth_alg - Allowed authentication algorithms + * Bit field of AUTH_ALG_* + */ + int auth_alg; + + /** + * mode - Operation mode (infra/ibss) IEEE80211_MODE_* + */ + int mode; + + /** + * wep_key - WEP keys for static WEP configuration + */ + const u8 *wep_key[4]; + + /** + * wep_key_len - WEP key length for static WEP configuration + */ + size_t wep_key_len[4]; + + /** + * wep_tx_keyidx - WEP TX key index for static WEP configuration + */ + int wep_tx_keyidx; + + /** + * mgmt_frame_protection - IEEE 802.11w management frame protection + */ + enum { + NO_MGMT_FRAME_PROTECTION, + MGMT_FRAME_PROTECTION_OPTIONAL, + MGMT_FRAME_PROTECTION_REQUIRED + } mgmt_frame_protection; + + /** + * ft_ies - IEEE 802.11r / FT information elements + * If the supplicant is using IEEE 802.11r (FT) and has the needed keys + * for fast transition, this parameter is set to include the IEs that + * are to be sent in the next FT Authentication Request message. + * update_ft_ies() handler is called to update the IEs for further + * FT messages in the sequence. + * + * The driver should use these IEs only if the target AP is advertising + * the same mobility domain as the one included in the MDIE here. + * + * In ap_scan=2 mode, the driver can use these IEs when moving to a new + * AP after the initial association. These IEs can only be used if the + * target AP is advertising support for FT and is using the same MDIE + * and SSID as the current AP. + * + * The driver is responsible for reporting the FT IEs received from the + * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE + * type. update_ft_ies() handler will then be called with the FT IEs to + * include in the next frame in the authentication sequence. + */ + const u8 *ft_ies; + + /** + * ft_ies_len - Length of ft_ies in bytes + */ + size_t ft_ies_len; + + /** + * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) + * + * This value is provided to allow the driver interface easier access + * to the current mobility domain. This value is set to %NULL if no + * mobility domain is currently active. + */ + const u8 *ft_md; + + /** + * passphrase - RSN passphrase for PSK + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 8..63 character ASCII passphrase, if available. Please note that + * this can be %NULL if passphrase was not used to generate the PSK. In + * that case, the psk field must be used to fetch the PSK. + */ + const char *passphrase; + + /** + * psk - RSN PSK (alternative for passphrase for PSK) + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 32-octet (256-bit) PSK, if available. The driver wrapper should + * be prepared to handle %NULL value as an error. + */ + const u8 *psk; +}; + +/** + * struct wpa_driver_capa - Driver capability information + */ +struct wpa_driver_capa { +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 + unsigned int key_mgmt; + +#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 +#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 +#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 +#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 + unsigned int enc; + +#define WPA_DRIVER_AUTH_OPEN 0x00000001 +#define WPA_DRIVER_AUTH_SHARED 0x00000002 +#define WPA_DRIVER_AUTH_LEAP 0x00000004 + unsigned int auth; + +/* Driver generated WPA/RSN IE */ +#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 +#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004 +/* Driver takes care of RSN 4-way handshake internally; PMK is configured with + * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ +#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 + unsigned int flags; +}; + + +#define WPA_CHAN_W_SCAN 0x00000001 +#define WPA_CHAN_W_ACTIVE_SCAN 0x00000002 +#define WPA_CHAN_W_IBSS 0x00000004 + +struct wpa_channel_data { + short chan; /* channel number (IEEE 802.11) */ + short freq; /* frequency in MHz */ + int flag; /* flag for user space use (WPA_CHAN_*) */ +}; + +#define WPA_RATE_ERP 0x00000001 +#define WPA_RATE_BASIC 0x00000002 +#define WPA_RATE_PREAMBLE2 0x00000004 +#define WPA_RATE_SUPPORTED 0x00000010 +#define WPA_RATE_OFDM 0x00000020 +#define WPA_RATE_CCK 0x00000040 +#define WPA_RATE_MANDATORY 0x00000100 + +struct wpa_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* WPA_RATE_ flags */ +}; + +typedef enum { + WPA_MODE_IEEE80211B, + WPA_MODE_IEEE80211G, + WPA_MODE_IEEE80211A, + NUM_WPA_MODES +} wpa_hw_mode; + +struct wpa_hw_modes { + wpa_hw_mode mode; + int num_channels; + struct wpa_channel_data *channels; + int num_rates; + struct wpa_rate_data *rates; +}; + + +struct ieee80211_rx_status { + int channel; + int ssi; +}; + + +/** + * struct wpa_driver_ops - Driver interface API definition + * + * This structure defines the API that each driver interface needs to implement + * for core wpa_supplicant code. All driver specific functionality is captured + * in this wrapper. + */ +struct wpa_driver_ops { + /** Name of the driver interface */ + const char *name; + /** One line description of the driver interface */ + const char *desc; + + /** + * get_bssid - Get the current BSSID + * @priv: private driver interface data + * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) + * + * Returns: 0 on success, -1 on failure + * + * Query kernel driver for the current BSSID and copy it to bssid. + * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ + int (*get_bssid)(void *priv, u8 *bssid); + + /** + * get_ssid - Get the current SSID + * @priv: private driver interface data + * @ssid: buffer for SSID (at least 32 bytes) + * + * Returns: Length of the SSID on success, -1 on failure + * + * Query kernel driver for the current SSID and copy it to ssid. + * Returning zero is recommended if the STA is not associated. + * + * Note: SSID is an array of octets, i.e., it is not nul terminated and + * can, at least in theory, contain control characters (including nul) + * and as such, should be processed as binary data, not a printable + * string. + */ + int (*get_ssid)(void *priv, u8 *ssid); + + /** + * set_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 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 + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for + * IGTK + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will 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 + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * + * Returns: Pointer to private data, %NULL on failure + * + * Initialize driver interface, including event processing for kernel + * driver events (e.g., associated, scan results, Michael MIC failure). + * This function can allocate a private configuration data area for + * @ctx, file descriptor, interface name, etc. information that may be + * needed in future driver operations. If this is not used, non-NULL + * value will need to be returned because %NULL is used to indicate + * failure. The returned value will be used as 'void *priv' data for + * all other driver_ops functions. + * + * The main event loop (eloop.c) of wpa_supplicant can be used to + * register callback for read sockets (eloop_register_read_sock()). + * + * See below for more information about events and + * wpa_supplicant_event() function. + */ + void * (*init)(void *ctx, const char *ifname); + + /** + * deinit - Deinitialize driver interface + * @priv: private driver interface data from init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in init() handler. + */ + void (*deinit)(void *priv); + + /** + * set_param - Set driver configuration parameters + * @priv: private driver interface data from init() + * @param: driver specific configuration parameters + * + * Returns: 0 on success, -1 on failure + * + * Optional handler for notifying driver interface about configuration + * parameters (driver_param). + */ + int (*set_param)(void *priv, const char *param); + + /** + * set_countermeasures - Enable/disable TKIP countermeasures + * @priv: private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * + * Returns: 0 on success, -1 on failure + * + * Configure TKIP countermeasures. When these are enabled, the driver + * should drop all received and queued frames that are using TKIP. + */ + int (*set_countermeasures)(void *priv, int enabled); + + /** + * set_drop_unencrypted - Enable/disable unencrypted frame filtering + * @priv: private driver interface data + * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled + * + * 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 + * still be allowed for key negotiation. + */ + int (*set_drop_unencrypted)(void *priv, int enabled); + + /** + * 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 + * + * Returns: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results(). + */ + int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); + + /** + * get_scan_results - Fetch the latest scan results (old version) + * @priv: private driver interface data + * @results: pointer to buffer for scan results + * @max_size: maximum number of entries (buffer size) + * + * 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 + * returned and the remaining entries will not be included in the + * buffer. + * + * This function is depracated. New driver wrapper implementations + * should implement support for get_scan_results2(). + */ + int (*get_scan_results)(void *priv, + struct wpa_scan_result *results, + size_t max_size); + + /** + * deauthenticate - Request driver to deauthenticate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the deauthentication + * frame + * + * Returns: 0 on success, -1 on failure + */ + int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + + /** + * 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 + * + * Returns: 0 on success, -1 on failure + */ + int (*disassociate)(void *priv, const u8 *addr, int reason_code); + + /** + * associate - Request driver to associate + * @priv: private driver interface data + * @params: association parameters + * + * Returns: 0 on success, -1 on failure + */ + int (*associate)(void *priv, + struct wpa_driver_associate_params *params); + + /** + * set_auth_alg - Set IEEE 802.11 authentication algorithm + * @priv: private driver interface data + * @auth_alg: bit field of AUTH_ALG_* + * + * If the driver supports more than one authentication algorithm at the + * same time, it should configure all supported algorithms. If not, one + * algorithm needs to be selected arbitrarily. Open System + * authentication should be ok for most cases and it is recommended to + * be used if other options are not supported. Static WEP configuration + * may also use Shared Key authentication and LEAP requires its own + * algorithm number. For LEAP, user can make sure that only one + * algorithm is used at a time by configuring LEAP as the only + * supported EAP method. This information is also available in + * associate() params, so set_auth_alg may not be needed in case of + * most drivers. + * + * 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 + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when a new PMK is received, as a result of + * either normal authentication or RSN pre-authentication. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), add_pmkid() can be used to add new PMKSA cache entries + * in the driver. If the driver uses wpa_ie from wpa_supplicant, this + * driver_ops function does not need to be implemented. Likewise, if + * the driver does not support WPA, this function is not needed. + */ + int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * remove_pmkid - Remove PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops a PMKSA cache + * entry for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * flush_pmkid - Flush PMKSA cache + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops all PMKSA cache + * entries for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*flush_pmkid)(void *priv); + + /** + * flush_pmkid - Flush PMKSA cache + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * Get driver/firmware/hardware capabilities. + */ + int (*get_capa)(void *priv, struct wpa_driver_capa *capa); + + /** + * poll - Poll driver for association information + * @priv: private driver interface data + * + * This is an option callback that can be used when the driver does not + * provide event mechanism for association events. This is called when + * receiving WPA EAPOL-Key messages that require association + * information. The driver interface is supposed to generate associnfo + * event before returning from this callback function. In addition, the + * driver interface should generate an association event after having + * sent out associnfo. + */ + void (*poll)(void *priv); + + /** + * get_ifname - Get interface name + * @priv: private driver interface data + * + * Returns: Pointer to the interface name. This can differ from the + * interface name used in init() call. Init() is called first. + * + * This optional function can be used to allow the driver interface to + * replace the interface name with something else, e.g., based on an + * interface mapping from a more descriptive name. + */ + const char * (*get_ifname)(void *priv); + + /** + * get_mac_addr - Get own MAC address + * @priv: private driver interface data + * + * Returns: Pointer to own MAC address or %NULL on failure + * + * This optional function can be used to get the own MAC address of the + * device from the driver interface code. This is only needed if the + * l2_packet implementation for the OS does not provide easy access to + * a MAC address. */ + const u8 * (*get_mac_addr)(void *priv); + + /** + * send_eapol - Optional function for sending EAPOL packets + * @priv: private driver interface data + * @dest: Destination MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Size of the EAPOL packet + * + * Returns: 0 on success, -1 on failure + * + * This optional function can be used to override l2_packet operations + * with driver specific functionality. If this function pointer is set, + * l2_packet module is not used at all and the driver interface code is + * responsible for receiving and sending all EAPOL packets. The + * received EAPOL packets are sent to core code 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); + + /** + * set_operstate - Sets device operating state to DORMANT or UP + * @priv: private driver interface data + * @state: 0 = dormant, 1 = up + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used on operating systems + * that support a concept of controlling network device state from user + * space applications. This function, if set, gets called with + * state = 1 when authentication has been completed and with state = 0 + * when connection is lost. + */ + int (*set_operstate)(void *priv, int state); + + /** + * mlme_setprotection - MLME-SETPROTECTION.request primitive + * @priv: Private driver interface data + * @addr: Address of the station for which to set protection (may be + * %NULL for group keys) + * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* + * @key_type: MLME_SETPROTECTION_KEY_TYPE_* + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used to set the driver to + * require protection for Tx and/or Rx frames. This uses the layer + * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 + * (MLME-SETPROTECTION.request). Many drivers do not use explicit + * set protection operation; instead, they set protection implicitly + * based on configured keys. + */ + int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type, + int key_type); + + /** + * get_hw_feature_data - Get hardware support data (channels and rates) + * @priv: Private driver interface data + * @num_modes: Variable for returning the number of returned modes + * flags: Variable for returning hardware feature flags + * Returns: Pointer to allocated hardware data on success or %NULL on + * failure. Caller is responsible for freeing this. + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + struct wpa_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); + + /** + * set_channel - Set channel + * @priv: Private driver interface data + * @phymode: WPA_MODE_IEEE80211B, .. + * @chan: IEEE 802.11 channel number + * @freq: Frequency of the channel in MHz + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + int (*set_channel)(void *priv, wpa_hw_mode phymode, int chan, + int freq); + + /** + * set_ssid - Set SSID + * @priv: Private driver interface data + * @ssid: SSID + * @ssid_len: SSID length + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len); + + /** + * set_bssid - Set BSSID + * @priv: Private driver interface data + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + int (*set_bssid)(void *priv, const u8 *bssid); + + /** + * send_mlme - Send management frame from MLME + * @priv: Private driver interface data + * @data: IEEE 802.11 management frame with IEEE 802.11 header + * @data_len: Size of the management frame + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + int (*send_mlme)(void *priv, const u8 *data, size_t data_len); + + /** + * mlme_add_sta - Add a STA entry into the driver/netstack + * @priv: Private driver interface data + * @addr: MAC address of the STA (e.g., BSSID of the AP) + * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11 + * format (one octet per rate, 1 = 0.5 Mbps) + * @supp_rates_len: Number of entries in supp_rates + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. When the MLME code + * completes association with an AP, this function is called to + * configure the driver/netstack with a STA entry for data frame + * processing (TX rate control, encryption/decryption). + */ + int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates, + size_t supp_rates_len); + + /** + * mlme_remove_sta - Remove a STA entry from the driver/netstack + * @priv: Private driver interface data + * @addr: MAC address of the STA (e.g., BSSID of the AP) + * Returns: 0 on success, -1 on failure + * + * This function is only needed for drivers that export MLME + * (management frame processing) to wpa_supplicant. + */ + int (*mlme_remove_sta)(void *priv, const u8 *addr); + + /** + * update_ft_ies - Update FT (IEEE 802.11r) IEs + * @priv: Private driver interface data + * @md: Mobility domain (2 octets) (also included inside ies) + * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs + * @ies_len: Length of FT IEs in bytes + * Returns: 0 on success, -1 on failure + * + * The supplicant uses this callback to let the driver know that keying + * material for FT is available and that the driver can use the + * provided IEs in the next message in FT authentication sequence. + * + * This function is only needed for driver that support IEEE 802.11r + * (Fast BSS Transition). + */ + int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, + size_t ies_len); + + /** + * send_ft_action - Send FT Action frame (IEEE 802.11r) + * @priv: Private driver interface data + * @action: Action field value + * @target_ap: Target AP address + * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body) + * @ies_len: Length of FT IEs in bytes + * Returns: 0 on success, -1 on failure + * + * The supplicant uses this callback to request the driver to transmit + * an FT Action frame (action category 6) for over-the-DS fast BSS + * transition. + */ + int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len); + + /** + * get_scan_results2 - Fetch the latest scan results + * @priv: private driver interface data + * + * Returns: Allocated buffer of scan results (caller is responsible for + * freeing the data structure) on success, NULL on failure + */ + struct wpa_scan_results * (*get_scan_results2)(void *priv); + + /** + * set_probe_req_ie - Set information element(s) for Probe Request + * @priv: private driver interface data + * @ies: Information elements to append or %NULL to remove extra IEs + * @ies_len: Length of the IE buffer in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_probe_req_ie)(void *priv, const u8 *ies, size_t ies_len); + + /** + * set_mode - Request driver to set the operating mode + * @priv: private driver interface data + * @mode: Operation mode (infra/ibss) IEEE80211_MODE_* + * + * This handler will be called before any key configuration and call to + * associate() handler in order to allow the operation mode to be + * configured as early as possible. This information is also available + * in associate() params and as such, some driver wrappers may not need + * to implement set_mode() handler. + * Returns: 0 on success, -1 on failure + */ + int (*set_mode)(void *priv, int mode); + + /** + * set_country - Set country + * @priv: Private driver interface data + * @alpha2: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This function is for drivers which support some form + * of setting a regulatory domain. + */ + int (*set_country)(void *priv, const char *alpha2); + + /** + * global_init - Global driver initialization + * Returns: Pointer to private data (global), %NULL on failure + * + * This optional function is called to initialize the driver wrapper + * for global data, i.e., data that applies to all interfaces. If this + * function is implemented, global_deinit() will also need to be + * implemented to free the private data. The driver will also likely + * use init2() function instead of init() to get the pointer to global + * data available to per-interface initializer. + */ + void * (*global_init)(void); + + /** + * global_deinit - Global driver deinitialization + * @priv: private driver global data from global_init() + * + * Terminate any global driver related functionality and free the + * global data structure. + */ + void (*global_deinit)(void *priv); + + /** + * init2 - Initialize driver interface (with global data) + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() + * Returns: Pointer to private data, %NULL on failure + * + * This function can be used instead of init() if the driver wrapper + * uses global data. + */ + void * (*init2)(void *ctx, const char *ifname, void *global_priv); + + /** + * get_interfaces - Get information about available interfaces + * @global_priv: private driver global data from global_init() + * Returns: Allocated buffer of interface information (caller is + * responsible for freeing the data structure) on success, NULL on + * failure + */ + struct wpa_interface_info * (*get_interfaces)(void *global_priv); +}; + +/* Function to check whether a driver is for wired connections */ +static inline int IS_WIRED(const struct wpa_driver_ops *drv) +{ + return os_strcmp(drv->name, "wired") == 0 || + os_strcmp(drv->name, "roboswitch") == 0; +} + +/** + * 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 event has been generated. In addition, optional + * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide + * more information about the association. If the driver interface gets + * both of these events at the same time, it can also include the + * assoc_info data in EVENT_ASSOC call. + */ + EVENT_ASSOC, + + /** + * EVENT_DISASSOC - Association lost + * + * This event should be called when association is lost either due to + * receiving deauthenticate or disassociate frame from the AP or when + * sending either of these frames to the current AP. + */ + EVENT_DISASSOC, + + /** + * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected + * + * This event must be delivered when a Michael MIC error is detected by + * the local driver. Additional data for event processing is + * provided with union wpa_event_data::michael_mic_failure. This + * information is used to request new encyption key and to initiate + * TKIP countermeasures if needed. + */ + EVENT_MICHAEL_MIC_FAILURE, + + /** + * EVENT_SCAN_RESULTS - Scan results available + * + * This event must be called whenever scan results are available to be + * fetched with struct wpa_driver_ops::get_scan_results(). This event + * is expected to be used some time after struct wpa_driver_ops::scan() + * is called. If the driver provides an unsolicited event when the scan + * has been completed, this event can be used to trigger + * EVENT_SCAN_RESULTS call. If such event is not available from the + * driver, the driver wrapper code is expected to use a registered + * timeout to generate EVENT_SCAN_RESULTS call after the time that the + * scan is expected to be completed. + */ + EVENT_SCAN_RESULTS, + + /** + * EVENT_ASSOCINFO - Report optional extra information for association + * + * This event can be used to report extra association information for + * EVENT_ASSOC processing. This extra information includes IEs from + * association frames and Beacon/Probe Response frames in union + * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before + * EVENT_ASSOC. Alternatively, the driver interface can include + * assoc_info data in the EVENT_ASSOC call if it has all the + * information available at the same point. + */ + EVENT_ASSOCINFO, + + /** + * EVENT_INTERFACE_STATUS - Report interface status changes + * + * This optional event can be used to report changes in interface + * status (interface added/removed) using union + * wpa_event_data::interface_status. This can be used to trigger + * wpa_supplicant to stop and re-start processing for the interface, + * e.g., when a cardbus card is ejected/inserted. + */ + EVENT_INTERFACE_STATUS, + + /** + * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication + * + * This event can be used to inform wpa_supplicant about candidates for + * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible + * for scan request (ap_scan=2 mode), this event is required for + * pre-authentication. If wpa_supplicant is performing scan request + * (ap_scan=1), this event is optional since scan results can be used + * to add pre-authentication candidates. union + * wpa_event_data::pmkid_candidate is used to report the BSSID of the + * candidate and priority of the candidate, e.g., based on the signal + * strength, in order to try to pre-authenticate first with candidates + * that are most likely targets for re-association. + * + * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates + * on the candidate list. In addition, it can be called for the current + * AP and APs that have existing PMKSA cache entries. wpa_supplicant + * will automatically skip pre-authentication in cases where a valid + * PMKSA exists. When more than one candidate exists, this event should + * be generated once for each candidate. + * + * Driver will be notified about successful pre-authentication with + * struct wpa_driver_ops::add_pmkid() calls. + */ + EVENT_PMKID_CANDIDATE, + + /** + * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request) + * + * This event can be used to inform wpa_supplicant about desire to set + * up secure direct link connection between two stations as defined in + * IEEE 802.11e with a new PeerKey mechanism that replaced the original + * STAKey negotiation. The caller will need to set peer address for the + * event. + */ + EVENT_STKSTART, + + /** + * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs + * + * The driver is expected to report the received FT IEs from + * FT authentication sequence from the AP. The FT IEs are included in + * the extra information in union wpa_event_data::ft_ies. + */ + EVENT_FT_RESPONSE +} wpa_event_type; + + +/** + * union wpa_event_data - Additional data for wpa_supplicant_event() calls + */ +union wpa_event_data { + /** + * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events + * + * This structure is optional for EVENT_ASSOC calls and required for + * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the + * driver interface does not need to generate separate EVENT_ASSOCINFO + * calls. + */ + struct assoc_info { + /** + * 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 + * set, the results from previous scan will be used. If no + * data for the new AP is found, scan results will be requested + * again (without scan request). At this point, the driver is + * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is + * used). + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *beacon_ies; + + /** + * beacon_ies_len - Length of beacon_ies */ + size_t beacon_ies_len; + } assoc_info; + + /** + * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE + */ + struct michael_mic_failure { + int unicast; + } michael_mic_failure; + + /** + * struct interface_status - Data for EVENT_INTERFACE_STATUS + */ + struct interface_status { + char ifname[100]; + enum { + EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED + } ievent; + } interface_status; + + /** + * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE + */ + struct pmkid_candidate { + /** BSSID of the PMKID candidate */ + u8 bssid[ETH_ALEN]; + /** Smaller the index, higher the priority */ + int index; + /** Whether RSN IE includes pre-authenticate flag */ + int preauth; + } pmkid_candidate; + + /** + * struct stkstart - Data for EVENT_STKSTART + */ + struct stkstart { + u8 peer[ETH_ALEN]; + } stkstart; + + /** + * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) + * + * During FT (IEEE 802.11r) authentication sequence, the driver is + * expected to use this event to report received FT IEs (MDIE, FTIE, + * RSN IE, TIE, possible resource request) to the supplicant. The FT + * IEs for the next message will be delivered through the + * struct wpa_driver_ops::update_ft_ies() callback. + */ + struct ft_ies { + const u8 *ies; + size_t ies_len; + int ft_action; + u8 target_ap[ETH_ALEN]; + } ft_ies; +}; + +/** + * wpa_supplicant_event - Report a driver event for wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Driver wrapper code should call this function whenever an event is received + * from the driver. + */ +void wpa_supplicant_event(void *ctx, wpa_event_type event, + union wpa_event_data *data); + +/** + * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @src_addr: Source address of the EAPOL frame + * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header) + * @len: Length of the EAPOL data + * + * This function is called for each received EAPOL frame. Most driver + * interfaces rely on more generic OS mechanism for receiving frames through + * l2_packet, but if such a mechanism is not available, the driver wrapper may + * take care of received EAPOL frames and deliver them to the core supplicant + * code by calling this function. + */ +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + +void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status); +void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features); + +const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); +#define WPA_IE_VENDOR_TYPE 0x0050f201 +#define WPS_IE_VENDOR_TYPE 0x0050f204 +const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, + u32 vendor_type); +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type); +int wpa_scan_get_max_rate(const struct wpa_scan_res *res); +void wpa_scan_results_free(struct wpa_scan_results *res); +void wpa_scan_sort_results(struct wpa_scan_results *res); + +#endif /* DRIVER_H */ diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c new file mode 100644 index 0000000000000..0a7a66ddb5f2e --- /dev/null +++ b/src/drivers/driver_atmel.c @@ -0,0 +1,506 @@ +/* + * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers + * Copyright (c) 2000-2005, ATMEL Corporation + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/****************************************************************************** + Copyright 2000-2001 ATMEL Corporation. + + WPA Supplicant - driver interaction with Atmel Wireless lan drivers. + + This is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 Atmel wireless lan drivers; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +******************************************************************************/ + +/* + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_atmel_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2) +#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3) +#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4) + + +/* ATMEL_WPA_IOCTL ioctl() cmd: */ +enum { + SET_WPA_ENCRYPTION = 1, + SET_CIPHER_SUITES = 2, + MLME_STA_DEAUTH = 3, + MLME_STA_DISASSOC = 4 +}; + +/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */ +enum { + ATMEL_PARAM_WPA = 1, + ATMEL_PARAM_PRIVACY_INVOKED = 2, + ATMEL_PARAM_WPA_TYPE = 3 +}; + +#define MAX_KEY_LENGTH 40 + +struct atmel_param{ + unsigned char sta_addr[6]; + int cmd; + u8 alg; + u8 key_idx; + u8 set_tx; + u8 seq[8]; + u8 seq_len; + u16 key_len; + u8 key[MAX_KEY_LENGTH]; + struct{ + int reason_code; + u8 state; + }mlme; + u8 pairwise_suite; + u8 group_suite; + u8 key_mgmt_suite; +}; + + + +static int atmel_ioctl(struct wpa_driver_atmel_data *drv, + struct atmel_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) { + int ret; + ret = errno; + if (show_err) + perror("ioctl[ATMEL_WPA_IOCTL]"); + return ret; + } + + return 0; +} + + +static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) { + perror("ioctl[ATMEL_WPA_IOCTL_PARAM]"); + ret = -1; + } + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv, + const char *wpa_ie, size_t wpa_ie_len) +{ + struct atmel_param *param; + int res; + size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = ATMEL_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = atmel_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} +#endif + + +static int wpa_driver_atmel_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + + printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname); + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + +#if 0 + if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; +#endif + if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0) + ret = -1; + if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_atmel_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) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + struct atmel_param *param; + u8 *buf; + u8 alg_type; + + size_t blen; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + alg_type = 0; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + alg_type = 1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + alg_type = 2; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + alg_type = 3; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct atmel_param *) buf; + + param->cmd = SET_WPA_ENCRYPTION; + + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); + + param->alg = alg_type; + param->key_idx = key_idx; + param->set_tx = set_tx; + os_memcpy(param->seq, seq, seq_len); + param->seq_len = seq_len; + param->key_len = key_len; + os_memcpy((u8 *)param->key, key, key_len); + + if (atmel_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + /* TODO: show key error*/ + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_atmel_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_set_drop_unencrypted(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_drop_unencrypted - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + struct atmel_param param; + int ret; + int mgmt_error = 0xaa; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = cmd; + param.mlme.reason_code = reason_code; + param.mlme.state = mgmt_error; + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv, + u8 pairwise_suite, u8 group_suite, + u8 key_mgmt_suite) +{ + struct atmel_param param; + int ret; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = SET_CIPHER_SUITES; + param.pairwise_suite = pairwise_suite; + param.group_suite = group_suite; + param.key_mgmt_suite = key_mgmt_suite; + + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} +#endif + + +static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_deauthenticate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); + +} + + +static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_disassociate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); + +} + + +#if 0 +/* Atmel driver uses specific values for each cipher suite */ +static int convertSuiteToDriver(wpa_cipher suite) +{ + u8 suite_type; + + switch(suite) { + case CIPHER_NONE: + suite_type = 0; + break; + case CIPHER_WEP40: + suite_type = 1; + break; + case CIPHER_TKIP: + suite_type = 2; + break; + case CIPHER_WEP104: + suite_type = 5; + break; + case CIPHER_CCMP: + suite_type = 3; + break; + default: + suite_type = 2; + } + + return suite_type; + +} +#endif + +static int +wpa_driver_atmel_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; +#if 0 + u8 pairwise_suite_driver; + u8 group_suite_driver; + u8 key_mgmt_suite_driver; + + pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite); + group_suite_driver = convertSuiteToDriver(params->group_suite); + key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite); + + if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver, + group_suite_driver, + key_mgmt_suite_driver) < 0){ + printf("wpa_driver_atmel_set_suites.\n"); + ret = -1; + } + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) { + printf("wpa_driver_atmel_set_freq.\n"); + ret = -1; + } +#endif + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) { + printf("FAILED : wpa_driver_atmel_set_ssid.\n"); + ret = -1; + } + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) { + printf("FAILED : wpa_driver_atmel_set_bssid.\n"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_atmel_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_atmel_set_operstate(void *priv, int state) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_atmel_init(void *ctx, const char *ifname) +{ + struct wpa_driver_atmel_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_atmel_deinit(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_atmel_ops = { + .name = "atmel", + .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", + .get_bssid = wpa_driver_atmel_get_bssid, + .get_ssid = wpa_driver_atmel_get_ssid, + .set_wpa = wpa_driver_atmel_set_wpa, + .set_key = wpa_driver_atmel_set_key, + .init = wpa_driver_atmel_init, + .deinit = wpa_driver_atmel_deinit, + .set_countermeasures = wpa_driver_atmel_set_countermeasures, + .set_drop_unencrypted = wpa_driver_atmel_set_drop_unencrypted, + .scan = wpa_driver_atmel_scan, + .get_scan_results2 = wpa_driver_atmel_get_scan_results, + .deauthenticate = wpa_driver_atmel_deauthenticate, + .disassociate = wpa_driver_atmel_disassociate, + .associate = wpa_driver_atmel_associate, + .set_operstate = wpa_driver_atmel_set_operstate, +}; diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c new file mode 100644 index 0000000000000..3600dae110468 --- /dev/null +++ b/src/drivers/driver_broadcom.c @@ -0,0 +1,604 @@ +/* + * WPA Supplicant - driver interaction with old Broadcom wl.o driver + * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru> + * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that the newer Broadcom driver ("hybrid Linux driver") supports + * Linux wireless extensions and does not need (or even work) with this old + * driver wrapper. Use driver_wext.c with that driver. + */ + +#include "includes.h" + +#include <sys/ioctl.h> + +#include "common.h" + +#if 0 +#include <netpacket/packet.h> +#include <net/ethernet.h> /* the L2 protocols */ +#else +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#endif +#include <net/if.h> +#include <typedefs.h> + +/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys + * WRT54G GPL tarball. */ +#include <wlioctl.h> + +#include "driver.h" +#include "eloop.h" + +struct wpa_driver_broadcom_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; +}; + + +#ifndef WLC_DEAUTHENTICATE +#define WLC_DEAUTHENTICATE 143 +#endif +#ifndef WLC_DEAUTHENTICATE_WITH_REASON +#define WLC_DEAUTHENTICATE_WITH_REASON 201 +#endif +#ifndef WLC_SET_TKIP_COUNTERMEASURES +#define WLC_SET_TKIP_COUNTERMEASURES 202 +#endif + +#if !defined(PSK_ENABLED) /* NEW driver interface */ +#define WL_VERSION 360130 +/* wireless authentication bit vector */ +#define WPA_ENABLED 1 +#define PSK_ENABLED 2 + +#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) +#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) +#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) + +#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY + +typedef wl_wsec_key_t wsec_key_t; +#endif + +typedef struct { + uint32 val; + struct ether_addr ea; + uint16 res; +} wlc_deauth_t; + + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx); + +static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, + void *buf, int len) +{ + struct ifreq ifr; + wl_ioctl_t ioc; + int ret = 0; + + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", + drv->ifname, cmd, len, buf); + /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ + + ioc.cmd = cmd; + ioc.buf = buf; + ioc.len = len; + os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); + ifr.ifr_data = (caddr_t) &ioc; + if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { + if (cmd != WLC_GET_MAGIC) + perror(ifr.ifr_name); + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", + cmd, ret); + } + + return ret; +} + +static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) + return 0; + + os_memset(bssid, 0, ETH_ALEN); + return -1; +} + +static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + + if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) + return -1; + + os_memcpy(ssid, s.SSID, s.SSID_len); + return s.SSID_len; +} + +static int wpa_driver_broadcom_set_wpa(void *priv, int enable) +{ + struct wpa_driver_broadcom_data *drv = priv; + unsigned int wauth, wsec; + struct ether_addr ea; + + os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); + if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + if (enable) { + wauth = PSK_ENABLED; + wsec = TKIP_ENABLED; + } else { + wauth = 255; + wsec &= ~(TKIP_ENABLED | AES_ENABLED); + } + + if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 122, &ea, sizeof(ea)); + + return 0; +} + +static int wpa_driver_broadcom_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) +{ + struct wpa_driver_broadcom_data *drv = priv; + int ret; + wsec_key_t wkt; + + os_memset(&wkt, 0, sizeof wkt); + wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", + set_tx ? "PRIMARY " : "", key_idx, alg); + if (key && key_len > 0) + wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); + + switch (alg) { + case WPA_ALG_NONE: + wkt.algo = CRYPTO_ALGO_OFF; + break; + case WPA_ALG_WEP: + wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ + break; + case WPA_ALG_TKIP: + wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ + break; + case WPA_ALG_CCMP: + wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; + * AES_OCB_MSDU, AES_OCB_MPDU? */ + break; + default: + wkt.algo = CRYPTO_ALGO_NALG; + break; + } + + if (seq && seq_len > 0) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); + + if (addr) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); + + wkt.index = key_idx; + wkt.len = key_len; + if (key && key_len > 0) { + os_memcpy(wkt.data, key, key_len); + if (key_len == 32) { + /* hack hack hack XXX */ + os_memcpy(&wkt.data[16], &key[24], 8); + os_memcpy(&wkt.data[24], &key[16], 8); + } + } + /* wkt.algo = CRYPTO_ALGO_...; */ + wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; + if (addr && set_tx) + os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); + ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); + if (addr && set_tx) { + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); + } + return ret; +} + + +static void wpa_driver_broadcom_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + wl_wpa_header_t *wwh; + union wpa_event_data data; + + if ((left = recv(sock, buf, sizeof buf, 0)) < 0) + return; + + wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); + + if ((size_t) left < sizeof(wl_wpa_header_t)) + return; + + wwh = (wl_wpa_header_t *) buf; + + if (wwh->snap.type != WL_WPA_ETHER_TYPE) + return; + if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) + return; + + os_memset(&data, 0, sizeof(data)); + + switch (wwh->type) { + case WLC_ASSOC_MSG: + left -= WL_WPA_HEADER_LEN; + wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", + left); + if (left > 0) { + data.assoc_info.resp_ies = os_malloc(left); + if (data.assoc_info.resp_ies == NULL) + return; + os_memcpy(data.assoc_info.resp_ies, + buf + WL_WPA_HEADER_LEN, left); + data.assoc_info.resp_ies_len = left; + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " + "into resp_ies", + data.assoc_info.resp_ies, left); + } + /* data.assoc_info.req_ies = NULL; */ + /* data.assoc_info.req_ies_len = 0; */ + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case WLC_DISASSOC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case WLC_PTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 1; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + case WLC_GTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 0; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + default: + wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", + wwh->type); + break; + } + os_free(data.assoc_info.resp_ies); +} + +static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_ll ll; + struct wpa_driver_broadcom_data *drv; + struct ifreq ifr; + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + + s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); + if (s < 0) { + perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_protocol = ntohs(ETH_P_802_2); + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_hatype = 0; + ll.sll_pkttype = PACKET_HOST; + ll.sll_halen = 0; + + if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, + NULL); + drv->event_sock = s; + + return drv; +} + +static void wpa_driver_broadcom_deinit(void *priv) +{ + struct wpa_driver_broadcom_data *drv = priv; + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static int wpa_driver_broadcom_set_countermeasures(void *priv, + int enabled) +{ +#if 0 + struct wpa_driver_broadcom_data *drv = priv; + /* FIX: ? */ + return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, + sizeof(enabled)); +#else + return 0; +#endif +} + +static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_broadcom_data *drv = priv; + /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ + int restrict = (enabled ? 1 : 0); + + if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, + &restrict, sizeof(restrict)) < 0 || + broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, + &restrict, sizeof(restrict)) < 0) + return -1; + + return 0; +} + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + +static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t wst = { 0, "" }; + + if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { + wst.SSID_len = ssid_len; + os_memcpy(wst.SSID, ssid, ssid_len); + } + + if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) + return -1; + + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static const int frequency_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +struct bss_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + /* u8 oui_type; */ + /* u16 version; */ +} __attribute__ ((packed)); + +static int +wpa_driver_broadcom_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_broadcom_data *drv = priv; + char *buf; + wl_scan_results_t *wsr; + wl_bss_info_t *wbi; + size_t ap_num; + + buf = os_malloc(WLC_IOCTL_MAXLEN); + if (buf == NULL) + return -1; + + wsr = (wl_scan_results_t *) buf; + + wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); + wsr->version = 107; + wsr->count = 0; + + if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { + int left; + struct bss_ie_hdr *ie; + + os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); + results[ap_num].ssid_len = wbi->SSID_len; + results[ap_num].freq = frequency_list[wbi->channel - 1]; + /* get ie's */ + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", + (u8 *) wbi + sizeof(*wbi), wbi->ie_length); + ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); + for (left = wbi->ie_length; left > 0; + left -= (ie->len + 2), ie = (struct bss_ie_hdr *) + ((u8 *) ie + 2 + ie->len)) { + wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", + ie->elem_id, ie->len); + if (ie->len >= 3) + wpa_printf(MSG_MSGDUMP, + "BROADCOM: oui:%02x%02x%02x", + ie->oui[0], ie->oui[1], ie->oui[2]); + if (ie->elem_id != 0xdd || + ie->len < 6 || + os_memcmp(ie->oui, WPA_OUI, 3) != 0) + continue; + os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); + results[ap_num].wpa_ie_len = ie->len + 2; + break; + } + + wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); + } + + wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " + "BSSes)", + wsr->buflen, (unsigned long) ap_num); + + os_free(buf); + return ap_num; +} + +static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_deauth_t wdt; + wdt.val = reason_code; + os_memcpy(&wdt.ea, addr, sizeof wdt.ea); + wdt.res = 0x7fff; + return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, + sizeof(wdt)); +} + +static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); +} + +static int +wpa_driver_broadcom_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + int infra = 1; + int auth = 0; + int wsec = 4; + int dummy; + int wpa_auth; + + s.SSID_len = params->ssid_len; + os_memcpy(s.SSID, params->ssid, params->ssid_len); + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + case CIPHER_WEP104: + wsec = 1; + break; + + case CIPHER_TKIP: + wsec = 2; + break; + + case CIPHER_CCMP: + wsec = 4; + break; + + default: + wsec = 0; + break; + } + + switch (params->key_mgmt_suite) { + case KEY_MGMT_802_1X: + wpa_auth = 1; + break; + + case KEY_MGMT_PSK: + wpa_auth = 2; + break; + + default: + wpa_auth = 255; + break; + } + + /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, + * group_suite, key_mgmt_suite); + * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); + * wl join uses wlc_sec_wep here, not wlc_set_wsec */ + + if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, + sizeof(wpa_auth)) < 0 || + broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || + broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || + broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || + broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) + return -1; + + return 0; +} + +const struct wpa_driver_ops wpa_driver_broadcom_ops = { + .name = "broadcom", + .desc = "Broadcom wl.o driver", + .get_bssid = wpa_driver_broadcom_get_bssid, + .get_ssid = wpa_driver_broadcom_get_ssid, + .set_wpa = wpa_driver_broadcom_set_wpa, + .set_key = wpa_driver_broadcom_set_key, + .init = wpa_driver_broadcom_init, + .deinit = wpa_driver_broadcom_deinit, + .set_countermeasures = wpa_driver_broadcom_set_countermeasures, + .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, + .scan = wpa_driver_broadcom_scan, + .get_scan_results = wpa_driver_broadcom_get_scan_results, + .deauthenticate = wpa_driver_broadcom_deauthenticate, + .disassociate = wpa_driver_broadcom_disassociate, + .associate = wpa_driver_broadcom_associate, +}; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c new file mode 100644 index 0000000000000..7debba4775087 --- /dev/null +++ b/src/drivers/driver_bsd.c @@ -0,0 +1,800 @@ +/* + * WPA Supplicant - driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include <net/if.h> + +#ifdef __NetBSD__ +#include <net/if_ether.h> +#define COMPAT_FREEBSD_NET80211 +#else +#include <net/ethernet.h> +#endif + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +struct wpa_driver_bsd_data { + int sock; /* open socket for 802.11 ioctls */ + int route; /* routing socket for events */ + char ifname[IFNAMSIZ+1]; /* interface name */ + unsigned int ifindex; /* interface index */ + void *ctx; + int prev_roaming; /* roaming state to restore on deinit */ + int prev_privacy; /* privacy state to restore on deinit */ + int prev_wpa; /* wpa state to restore on deinit */ +}; + +static int +set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = (void *) arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = arg; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return ireq.i_len; +} + +static int +set80211param(struct wpa_driver_bsd_data *drv, int op, int arg) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_val = arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n", + op, arg, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211param(struct wpa_driver_bsd_data *drv, int op) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n", + op, strerror(errno)); + return -1; + } + return ireq.i_val; +} + +static int +getifflags(struct wpa_driver_bsd_data *drv, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCGIFFLAGS"); + return errno; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + +static int +setifflags(struct wpa_driver_bsd_data *drv, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCSIFFLAGS"); + return errno; + } + return 0; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +} + +#if 0 +static int +wpa_driver_bsd_set_bssid(void *priv, const char *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN); +} +#endif + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_SSID, + ssid, IEEE80211_NWID_LEN); +} + +static int +wpa_driver_bsd_set_ssid(void *priv, const char *ssid, + size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv, + const char *wpa_ie, size_t wpa_ie_len) +{ + return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + struct wpa_driver_bsd_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx, + const unsigned char *addr) +{ + struct ieee80211req_del_key wk; + + os_memset(&wk, 0, sizeof(wk)); + if (addr != NULL && + bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) { + struct ether_addr ea; + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d", + __func__, ether_ntoa(&ea), key_idx); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE; + } else { + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx); + wk.idk_keyix = key_idx; + } + return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_key(void *priv, wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_key wk; + struct ether_addr ea; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_bsd_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __func__, alg); + return -1; + } + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, + "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu", + __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx, + seq_len, key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big", + __func__, seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %zu too big", + __func__, key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_XMIT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + /* + * Deduce whether group/global or unicast key by checking + * the address (yech). Note also that we can only mark global + * keys default; doing this for a unicast key is an error. + */ + if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) { + wk.ik_flags |= IEEE80211_KEY_GROUP; + wk.ik_keyix = key_idx; + } else { + wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx); + } + if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + wk.ik_keylen = key_len; + os_memcpy(&wk.ik_keyrsc, seq, seq_len); + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled); +} + + +static int +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} + +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + int privacy; + + wpa_printf(MSG_DEBUG, + "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" + , __func__ + , params->ssid_len, params->ssid + , params->wpa_ie_len + , params->pairwise_suite + , params->group_suite + , params->key_mgmt_suite + ); + + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + return -1; +#ifndef NEW_FREEBSD_MLME_ASSOC + if (wpa_driver_bsd_set_ssid(drv, params->ssid, params->ssid_len) < 0) + return -1; +#endif + + privacy = !(params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0); + wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); + + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + return -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_IOC_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0) + return -1; + + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; +#ifdef NEW_FREEBSD_MLME_ASSOC + if (params->ssid != NULL) + os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len); + mlme.im_ssid_len = params->ssid_len; +#endif + if (params->bssid != NULL) + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) + return -1; + return 0; +} + +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_bsd_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static int +wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + /* NB: interface must be marked UP to do a scan */ + if (getifflags(drv, &flags) != 0 || setifflags(drv, flags | IFF_UP) != 0) + return -1; + + /* set desired ssid before scan */ + if (wpa_driver_bsd_set_ssid(drv, ssid, ssid_len) < 0) + return -1; + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); +} + +#include <net/route.h> +#if __FreeBSD__ +#include <net80211/ieee80211_freebsd.h> +#endif +#if __NetBSD__ +#include <net80211/ieee80211_netbsd.h> +#endif + +static void +wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct wpa_driver_bsd_data *drv = sock_ctx; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct if_msghdr *ifm; + struct rt_msghdr *rtm; + union wpa_event_data event; + struct ieee80211_michael_event *mic; + int n; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + os_memset(&event, 0, sizeof(event)); + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + switch (ifan->ifan_what) { + case IFAN_DEPARTURE: + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + default: + return; + } + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", + event.interface_status.ifname, + ifan->ifan_what == IFAN_DEPARTURE ? + "removed" : "added"); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case RTM_IEEE80211_DISASSOC: + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case RTM_IEEE80211_SCAN: + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = + !IEEE80211_IS_MULTICAST(mic->iev_dst); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + break; + } + break; + case RTM_IFINFO: + ifm = (struct if_msghdr *) rtm; + if (ifm->ifm_index != drv->ifindex) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) { + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + event.interface_status.ifname); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + } + break; + } +} + +/* Compare function for sorting scan results. Return >0 if @b is consider + * better. */ +static int +wpa_scan_result_compar(const void *a, const void *b) +{ + const struct wpa_scan_result *wa = a; + const struct wpa_scan_result *wb = b; + + /* WPA/WPA2 support preferred */ + if ((wb->wpa_ie_len || wb->rsn_ie_len) && + !(wa->wpa_ie_len || wa->rsn_ie_len)) + return 1; + if (!(wb->wpa_ie_len || wb->rsn_ie_len) && + (wa->wpa_ie_len || wa->rsn_ie_len)) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) && + (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0) + return 1; + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAPINFO_PRIVACY)) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) + return wb->maxrate - wa->maxrate; + + /* use freq for channel preference */ + + /* all things being equal, use signal level */ + return wb->level - wa->level; +} + +static int +getmaxrate(uint8_t rates[15], uint8_t nrates) +{ + int i, maxrate = -1; + + for (i = 0; i < nrates; i++) { + int rate = rates[i] & IEEE80211_RATE_VAL; + if (rate > maxrate) + rate = maxrate; + } + return maxrate; +} + +/* unalligned little endian access */ +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int +wpa_driver_bsd_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ +#define min(a,b) ((a)>(b)?(b):(a)) + struct wpa_driver_bsd_data *drv = priv; + uint8_t buf[24*1024]; + uint8_t *cp, *vp; + struct ieee80211req_scan_result *sr; + struct wpa_scan_result *wsr; + int len, ielen; + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf)); + if (len < 0) + return -1; + cp = buf; + wsr = results; + while (len >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *) cp; + os_memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN); + wsr->ssid_len = sr->isr_ssid_len; + wsr->freq = sr->isr_freq; + wsr->noise = sr->isr_noise; + wsr->qual = sr->isr_rssi; + wsr->level = 0; /* XXX? */ + wsr->caps = sr->isr_capinfo; + wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates); + vp = (u_int8_t *)(sr+1); + os_memcpy(wsr->ssid, vp, sr->isr_ssid_len); + if (sr->isr_ie_len > 0) { + vp += sr->isr_ssid_len; + ielen = sr->isr_ie_len; + while (ielen > 0) { + switch (vp[0]) { + case IEEE80211_ELEMID_VENDOR: + if (!iswpaoui(vp)) + break; + wsr->wpa_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->wpa_ie, vp, + wsr->wpa_ie_len); + break; + case IEEE80211_ELEMID_RSN: + wsr->rsn_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->rsn_ie, vp, + wsr->rsn_ie_len); + break; + } + ielen -= 2+vp[1]; + vp += 2+vp[1]; + } + } + + cp += sr->isr_len, len -= sr->isr_len; + wsr++; + } + qsort(results, wsr - results, sizeof(struct wpa_scan_result), + wpa_scan_result_compar); + + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)", + len, wsr - results); + + return wsr - results; +#undef min +} + +static void * +wpa_driver_bsd_init(void *ctx, const char *ifname) +{ +#define GETPARAM(drv, param, v) \ + (((v) = get80211param(drv, param)) != -1) + struct wpa_driver_bsd_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + /* + * NB: We require the interface name be mappable to an index. + * This implies we do not support having wpa_supplicant + * wait for an interface to appear. This seems ok; that + * doesn't belong here; it's really the job of devd. + */ + drv->ifindex = if_nametoindex(ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, ifname); + goto fail1; + } + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail1; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) + goto fail; + eloop_register_read_sock(drv->route, + wpa_driver_bsd_event_receive, ctx, drv); + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { + wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { + wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { + wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", + __func__, strerror(errno)); + goto fail; + } + if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming: %s", __func__, strerror(errno)); + goto fail; + } + + if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s", + __func__, strerror(errno)); + goto fail; + } + + return drv; +fail: + close(drv->sock); +fail1: + os_free(drv); + return NULL; +#undef GETPARAM +} + +static void +wpa_driver_bsd_deinit(void *priv) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + eloop_unregister_read_sock(drv->route); + + /* NB: mark interface down */ + if (getifflags(drv, &flags) == 0) + (void) setifflags(drv, flags &~ IFF_UP); + + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) + wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", + __func__); + + (void) close(drv->route); /* ioctl socket */ + (void) close(drv->sock); /* event socket */ + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .desc = "BSD 802.11 support (Atheros, etc.)", + .init = wpa_driver_bsd_init, + .deinit = wpa_driver_bsd_deinit, + .get_bssid = wpa_driver_bsd_get_bssid, + .get_ssid = wpa_driver_bsd_get_ssid, + .set_wpa = wpa_driver_bsd_set_wpa, + .set_key = wpa_driver_bsd_set_key, + .set_countermeasures = wpa_driver_bsd_set_countermeasures, + .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted, + .scan = wpa_driver_bsd_scan, + .get_scan_results = wpa_driver_bsd_get_scan_results, + .deauthenticate = wpa_driver_bsd_deauthenticate, + .disassociate = wpa_driver_bsd_disassociate, + .associate = wpa_driver_bsd_associate, + .set_auth_alg = wpa_driver_bsd_set_auth_alg, +}; diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c new file mode 100644 index 0000000000000..84ef3bdedc549 --- /dev/null +++ b/src/drivers/driver_hostap.c @@ -0,0 +1,513 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "driver_hostap.h" + + +struct wpa_driver_hostap_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; + int current_mode; /* infra/adhoc */ +}; + + +static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int prism2param(struct wpa_driver_hostap_data *drv, int param, + int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_hostap_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) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); +} + + +static int wpa_driver_hostap_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, enabled); +} + + +static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, + int type) +{ + struct iwreq iwr; + int *i, ret = 0; + + wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = type; + + if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_RESET]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct prism2_hostapd_param param; + int ret; + + /* There does not seem to be a better way of deauthenticating or + * disassociating with Prism2/2.5/3 than sending the management frame + * and then resetting the Port0 to make sure both the AP and the STA + * end up in disconnected state. */ + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_MLME; + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.mlme.cmd = cmd; + param.u.mlme.reason_code = reason_code; + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + if (ret == 0) { + os_sleep(0, 100000); + ret = wpa_driver_hostap_reset(drv, 2); + } + return ret; +} + + +static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); +} + + +static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); +} + + +static int +wpa_driver_hostap_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode != drv->current_mode) { + /* At the moment, Host AP driver requires host_roaming=2 for + * infrastructure mode and host_roaming=0 for adhoc. */ + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, + params->mode == IEEE80211_MODE_IBSS ? 0 : 2) < + 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming", + __func__); + } + drv->current_mode = params->mode; + } + + if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + params->key_mgmt_suite != KEY_MGMT_NONE) < 0) + ret = -1; + if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0) + ret = -1; + if (params->freq && + wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X, + allow_unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "hostap: Failed to configure " + "ieee_802_1x param"); + /* Ignore this error.. driver_hostap.c can also be used with + * other drivers that do not support this prism2_param. */ + } + + return ret; +} + + +static int wpa_driver_hostap_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param param; + int ret; + + if (ssid == NULL) { + /* Use standard Linux Wireless Extensions ioctl if possible + * because some drivers using hostap code in wpa_supplicant + * might not support Host AP specific scan request (with SSID + * info). */ + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + } + + if (ssid_len > 32) + ssid_len = 32; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SCAN_REQ; + param.u.scan_req.ssid_len = ssid_len; + os_memcpy(param.u.scan_req.ssid, ssid, ssid_len); + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + + +static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_hostap_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs); +} + + +static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_hostap_set_operstate(void *priv, int state) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_hostap_init(void *ctx, const char *ifname) +{ + struct wpa_driver_hostap_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket"); + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + if (os_strncmp(ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); + } + + return drv; +} + + +static void wpa_driver_hostap_deinit(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .desc = "Host AP driver (Intersil Prism2/2.5/3)", + .get_bssid = wpa_driver_hostap_get_bssid, + .get_ssid = wpa_driver_hostap_get_ssid, + .set_wpa = wpa_driver_hostap_set_wpa, + .set_key = wpa_driver_hostap_set_key, + .set_countermeasures = wpa_driver_hostap_set_countermeasures, + .set_drop_unencrypted = wpa_driver_hostap_set_drop_unencrypted, + .scan = wpa_driver_hostap_scan, + .get_scan_results2 = wpa_driver_hostap_get_scan_results, + .deauthenticate = wpa_driver_hostap_deauthenticate, + .disassociate = wpa_driver_hostap_disassociate, + .associate = wpa_driver_hostap_associate, + .set_auth_alg = wpa_driver_hostap_set_auth_alg, + .init = wpa_driver_hostap_init, + .deinit = wpa_driver_hostap_deinit, + .set_operstate = wpa_driver_hostap_set_operstate, +}; diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h new file mode 100644 index 0000000000000..a2508ed924f68 --- /dev/null +++ b/src/drivers/driver_hostap.h @@ -0,0 +1,153 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef 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 \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 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/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m new file mode 100644 index 0000000000000..8e64ffdcc08a1 --- /dev/null +++ b/src/drivers/driver_iphone.m @@ -0,0 +1,466 @@ +/* + * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include "MobileApple80211.h" + +struct wpa_driver_iphone_data { + void *ctx; + Apple80211Ref wireless_ctx; + CFArrayRef scan_results; + int ctrl_power; +}; + + +static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key) +{ + const void *res; + CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key, + kCFStringEncodingMacRoman); + if (str == NULL) + return NULL; + + res = CFDictionaryGetValue(dict, str); + CFRelease(str); + return res; +} + + +static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFDataRef data; + int err, len; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) " + "failed: %d", err); + return -1; + } + + len = CFDataGetLength(data); + if (len > 32) { + CFRelease(data); + return -1; + } + os_memcpy(ssid, CFDataGetBytePtr(data), len); + CFRelease(data); + + return len; +} + + +static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFStringRef data; + int err; + int a1, a2, a3, a4, a5, a6; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) " + "failed: %d", err); + return -1; + } + + sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6); + bssid[0] = a1; + bssid[1] = a2; + bssid[2] = a3; + bssid[3] = a4; + bssid[4] = a5; + bssid[5] = a6; + + CFRelease(data); + + return 0; +} + + +static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d", + err); + return -1; + } + + eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_iphone_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_iphone_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + CFStringRef str; + CFNumberRef num; + int val; + + data = cfdict_get_key_str(dict, "SSID"); + if (data) { + res->ssid_len = CFDataGetLength(data); + if (res->ssid_len > 32) + res->ssid_len = 32; + os_memcpy(res->ssid, CFDataGetBytePtr(data), + res->ssid_len); + } + + str = cfdict_get_key_str(dict, "BSSID"); + if (str) { + int a1, a2, a3, a4, a5, a6; + sscanf(CFStringGetCStringPtr( + str, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", + &a1, &a2, &a3, &a4, &a5, &a6); + res->bssid[0] = a1; + res->bssid[1] = a2; + res->bssid[2] = a3; + res->bssid[3] = a4; + res->bssid[4] = a5; + res->bssid[5] = a6; + } + + num = cfdict_get_key_str(dict, "CAPABILITIES"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->caps = val; + } + + num = cfdict_get_key_str(dict, "CHANNEL"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->freq = 2407 + val * 5; + } + + num = cfdict_get_key_str(dict, "RSSI"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->level = val; + } + + num = cfdict_get_key_str(dict, "NOISE"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->noise = val; + } + + data = cfdict_get_key_str(dict, "IE"); + if (data) { + u8 *ptr = (u8 *) CFDataGetBytePtr(data); + int len = CFDataGetLength(data); + u8 *pos = ptr, *end = ptr + len; + + while (pos + 2 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_RSN && + pos[1] <= SSID_MAX_WPA_IE_LEN) { + os_memcpy(res->rsn_ie, pos, + 2 + pos[1]); + res->rsn_ie_len = 2 + pos[1]; + } + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] > 4 && pos[2] == 0x00 && + pos[3] == 0x50 && pos[4] == 0xf2 && + pos[5] == 0x01) { + os_memcpy(res->wpa_ie, pos, + 2 + pos[1]); + res->wpa_ie_len = 2 + pos[1]; + } + + pos = pos + 2 + pos[1]; + } + } + } + + return num; +} + + +static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_iphone_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + + if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout, + drv, drv->ctx); + return; + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_iphone_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_iphone_data *drv = priv; + int i, num, err; + size_t ssid_len; + CFDictionaryRef bss = NULL; + + /* + * TODO: Consider generating parameters instead of just using an entry + * from scan results in order to support ap_scan=2. + */ + + if (drv->scan_results == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot " + "associate"); + return -1; + } + + num = CFArrayGetCount(drv->scan_results); + + for (i = 0; i < num; i++) { + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + + data = cfdict_get_key_str(dict, "SSID"); + if (data == NULL) + continue; + + ssid_len = CFDataGetLength(data); + if (ssid_len != params->ssid_len || + os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len) + != 0) + continue; + + bss = dict; + break; + } + + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan " + "results - cannot associate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found " + "from scan results"); + + err = Apple80211Associate(drv->wireless_ctx, bss, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: " + "%d", err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_iphone_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) +{ + /* + * TODO: Need to either support configuring PMK for 4-way handshake or + * PTK for TKIP/CCMP. + */ + return -1; +} + + +static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_iphone_init(void *ctx, const char *ifname) +{ + struct wpa_driver_iphone_data *drv; + int err; + char power; + CFStringRef name; + CFDictionaryRef dict; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = Apple80211Open(&drv->wireless_ctx); + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d", + err); + os_free(drv); + return NULL; + } + + name = CFStringCreateWithCString(kCFAllocatorDefault, ifname, + kCFStringEncodingISOLatin1); + if (name == NULL) { + wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed"); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211BindToInterface(drv->wireless_ctx, name); + CFRelease(name); + + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211GetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d", + err); + + wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power); + + if (!power) { + drv->ctrl_power = 1; + err = Apple80211SetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict); + if (err == 0) { + CFShow(dict); + CFRelease(dict); + } else { + printf("Apple80211GetInfoCopy: %d\n", err); + } + + return drv; +} + + +static void wpa_driver_iphone_deinit(void *priv) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + + if (drv->ctrl_power) { + wpa_printf(MSG_DEBUG, "iPhone: Power down the interface"); + err = Apple80211SetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) " + "failed: %d", err); + } + } + + err = Apple80211Close(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d", + err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_iphone_ops = { + .name = "iphone", + .desc = "iPhone/iPod touch Apple80211 driver", + .get_ssid = wpa_driver_iphone_get_ssid, + .get_bssid = wpa_driver_iphone_get_bssid, + .init = wpa_driver_iphone_init, + .deinit = wpa_driver_iphone_deinit, + .scan = wpa_driver_iphone_scan, + .get_scan_results = wpa_driver_iphone_get_scan_results, + .associate = wpa_driver_iphone_associate, + .set_key = wpa_driver_iphone_set_key, + .get_capa = wpa_driver_iphone_get_capa, +}; diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c new file mode 100644 index 0000000000000..3c19cccf46405 --- /dev/null +++ b/src/drivers/driver_ipw.c @@ -0,0 +1,463 @@ +/* + * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers + * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com> + * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk> + * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that ipw2100/2200 drivers change to use generic Linux wireless + * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13 + * or newer). driver_wext.c should be used in those cases. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ipw_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */ + +#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 + +#define IPW_CMD_SET_WPA_PARAM 1 +#define IPW_CMD_SET_WPA_IE 2 +#define IPW_CMD_SET_ENCRYPTION 3 +#define IPW_CMD_MLME 4 + +#define IPW_PARAM_WPA_ENABLED 1 +#define IPW_PARAM_TKIP_COUNTERMEASURES 2 +#define IPW_PARAM_DROP_UNENCRYPTED 3 +#define IPW_PARAM_PRIVACY_INVOKED 4 +#define IPW_PARAM_AUTH_ALGS 5 +#define IPW_PARAM_IEEE_802_1X 6 + +#define IPW_MLME_STA_DEAUTH 1 +#define IPW_MLME_STA_DISASSOC 2 + +#define IPW_CRYPT_ERR_UNKNOWN_ALG 2 +#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3 +#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define IPW_CRYPT_ERR_KEY_SET_FAILED 5 +#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7 + +#define IPW_CRYPT_ALG_NAME_LEN 16 + +struct ipw_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 reserved[32]; + u8 data[0]; + } wpa_ie; + struct{ + u32 command; + u32 reason_code; + } mlme; + struct { + u8 alg[IPW_CRYPT_ALG_NAME_LEN]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}; + +/* end of ipw2100.c and ipw2200.c code */ + +static int ipw_ioctl(struct wpa_driver_ipw_data *drv, + struct ipw_param *param, int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]"); + return ret; + } + + return 0; +} + + +static void ipw_show_set_key_error(struct ipw_param *param) +{ + switch (param->u.crypt.err) { + case IPW_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for" + " WEP."); + break; + case IPW_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case IPW_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case IPW_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case IPW_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case IPW_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct ipw_param *param; + int ret; + size_t blen = sizeof(*param) + wpa_ie_len; + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = IPW_CMD_SET_WPA_IE; + param->u.wpa_ie.len = wpa_ie_len; + os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len); + + ret = ipw_ioctl(drv, param, blen, 1); + + os_free(param); + return ret; +} + + +static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name, + u32 value) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = IPW_CMD_SET_WPA_PARAM; + param.u.wpa_param.name = name; + param.u.wpa_param.value = value; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr, + int cmd, int reason) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = IPW_CMD_MLME; + param.u.mlme.command = cmd; + param.u.mlme.reason_code = reason; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int wpa_driver_ipw_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_ipw_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) +{ + struct wpa_driver_ipw_data *drv = priv; + struct ipw_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct ipw_param *) buf; + param->cmd = IPW_CMD_SET_ENCRYPTION; + os_memset(param->sta_addr, 0xff, ETH_ALEN); + os_strlcpy((char *) param->u.crypt.alg, alg_name, + IPW_CRYPT_ALG_NAME_LEN); + param->u.crypt.set_tx = set_tx ? 1 : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (ipw_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + ipw_show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES, + enabled); + +} + + +static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code); +} + + +static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code); +} + + +static int +wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + int unencrypted_eapol; + + if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + unencrypted_eapol = 0; + else + unencrypted_eapol = 1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X, + unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "ipw: Failed to configure " + "ieee_802_1x param"); + } + + return ret; +} + + +static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ipw_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs); + return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs); +} + + +static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_ipw_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_ipw_set_operstate(void *priv, int state) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_ipw_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ipw_data *drv; + int ver; + + wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__); + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + ver = wpa_driver_wext_get_version(drv->wext); + if (ver >= 18) { + wpa_printf(MSG_WARNING, "Linux wireless extensions version %d " + "detected.", ver); + wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext " + "(-Dwext) instead of driver_ipw."); + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_ipw_deinit(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ipw_ops = { + .name = "ipw", + .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 " + "or newer)", + .get_bssid = wpa_driver_ipw_get_bssid, + .get_ssid = wpa_driver_ipw_get_ssid, + .set_wpa = wpa_driver_ipw_set_wpa, + .set_key = wpa_driver_ipw_set_key, + .set_countermeasures = wpa_driver_ipw_set_countermeasures, + .set_drop_unencrypted = wpa_driver_ipw_set_drop_unencrypted, + .scan = wpa_driver_ipw_scan, + .get_scan_results2 = wpa_driver_ipw_get_scan_results, + .deauthenticate = wpa_driver_ipw_deauthenticate, + .disassociate = wpa_driver_ipw_disassociate, + .associate = wpa_driver_ipw_associate, + .set_auth_alg = wpa_driver_ipw_set_auth_alg, + .init = wpa_driver_ipw_init, + .deinit = wpa_driver_ipw_deinit, + .set_operstate = wpa_driver_ipw_set_operstate, +}; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c new file mode 100644 index 0000000000000..cc6f27e7fe205 --- /dev/null +++ b/src/drivers/driver_madwifi.c @@ -0,0 +1,597 @@ +/* + * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "wireless_copy.h" + +/* + * Avoid conflicts with wpa_supplicant definitions by undefining a definition. + */ +#undef WME_OUI_TYPE + +#include <include/compat.h> +#include <net80211/ieee80211.h> +#ifdef WME_NUM_AC +/* Assume this is built against BSD branch of madwifi driver. */ +#define MADWIFI_BSD +#include <net80211/_ieee80211.h> +#endif /* WME_NUM_AC */ +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + + +#ifdef IEEE80211_IOCTL_SETWMMPARAMS +/* Assume this is built against madwifi-ng */ +#define MADWIFI_NG +#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +struct wpa_driver_madwifi_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +static int +set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (len < IFNAMSIZ && + op != IEEE80211_IOCTL_SET_APPIEBUF) { + /* + * Argument data fits inline; put it there. + */ + os_memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->sock, op, &iwr) < 0) { + if (show_err) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_KICKMAC; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + NULL, + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + NULL, + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_CHANLIST; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_CHANLIST]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && op <= last && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) + && opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + } + return -1; + } + return 0; +} + +static int +set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = op; + os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + if (show_err) + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* NB: SETOPTIE is not fixed-size so must not be inlined */ + iwr.u.data.pointer = (void *) wpa_ie; + iwr.u.data.length = wpa_ie_len; + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, + const u8 *addr) +{ + struct ieee80211req_del_key wk; + + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx); + os_memset(&wk, 0, sizeof(wk)); + wk.idk_keyix = key_idx; + if (addr != NULL) + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + + return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_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) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_key wk; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_madwifi_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* + * madwifi did not seem to like static WEP key + * configuration with IEEE80211_IOCTL_SETKEY, so use + * Linux wireless extensions ioctl for this. + */ + return wpa_driver_wext_set_key(drv->wext, alg, addr, + key_idx, set_tx, + seq, seq_len, + key, key_len); + } + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __FUNCTION__, alg); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big", + __FUNCTION__, (unsigned long) seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %lu too big", + __FUNCTION__, (unsigned long) key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + wk.ik_flags |= IEEE80211_KEY_GROUP; + if (set_tx) { + wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + } else + os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_keylen = key_len; +#ifdef WORDS_BIGENDIAN +#define WPA_KEY_RSC_LEN 8 + { + size_t i; + u8 tmp[WPA_KEY_RSC_LEN]; + os_memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < seq_len; i++) + tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i]; + os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); + } +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); +} + + +static int +wpa_driver_madwifi_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, enabled, 1); +} + +static int +wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret = 0, privacy = 1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * NB: Don't need to set the freq or cipher-related state as + * this is implied by the bssid which is used to locate + * the scanned node state which holds it. The ssid is + * needed to disambiguate an AP that broadcasts multiple + * ssid's but uses the same bssid. + */ + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + + if (params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0) + privacy = 0; + + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) + ret = -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_PARAM_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) + ret = -1; + + if (params->bssid == NULL) { + /* ap_scan=2 mode - driver takes care of AP selection and + * roaming */ + /* FIX: this does not seem to work; would probably need to + * change something in the driver */ + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) + ret = -1; + + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + } else { + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme), 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed", + __func__); + ret = -1; + } + } + + return ret; +} + +static int +wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_madwifi_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); +} + +static int +wpa_driver_madwifi_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + /* set desired ssid before scan */ + /* FIX: scan should not break the current association, so using + * set_ssid may not be the best way of doing this.. */ + if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0) + ret = -1; + + if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* + * madwifi delivers a scan complete event so no need to poll, but + * register a backup timeout anyway to make sure that we recover even + * if the driver does not send this event for any reason. This timeout + * will only be used if the event is not delivered (event handler will + * cancel the timeout). + */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + +static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * +wpa_driver_madwifi_get_scan_results(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_madwifi_set_operstate(void *priv, int state) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct ieee80211req_getset_appiebuf *probe_req_ie; + int ret; + + probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); + if (probe_req_ie == NULL) + return -1; + + probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ; + probe_req_ie->app_buflen = ies_len; + os_memcpy(probe_req_ie->app_buf, ies, ies_len); + + ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + ies_len, 1); + + os_free(probe_req_ie); + + return ret; +} + + +static void * wpa_driver_madwifi_init(void *ctx, const char *ifname) +{ + struct wpa_driver_madwifi_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail2; + + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming", __FUNCTION__); + goto fail3; + } + + if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", + __FUNCTION__); + goto fail3; + } + + return drv; + +fail3: + close(drv->sock); +fail2: + wpa_driver_wext_deinit(drv->wext); +fail: + os_free(drv); + return NULL; +} + + +static void wpa_driver_madwifi_deinit(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + + if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE", + __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based " + "roaming", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy " + "flag", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable WPA", + __FUNCTION__); + } + + wpa_driver_wext_deinit(drv->wext); + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .desc = "MADWIFI 802.11 support (Atheros, etc.)", + .get_bssid = wpa_driver_madwifi_get_bssid, + .get_ssid = wpa_driver_madwifi_get_ssid, + .set_key = wpa_driver_madwifi_set_key, + .init = wpa_driver_madwifi_init, + .deinit = wpa_driver_madwifi_deinit, + .set_countermeasures = wpa_driver_madwifi_set_countermeasures, + .set_drop_unencrypted = wpa_driver_madwifi_set_drop_unencrypted, + .scan = wpa_driver_madwifi_scan, + .get_scan_results2 = wpa_driver_madwifi_get_scan_results, + .deauthenticate = wpa_driver_madwifi_deauthenticate, + .disassociate = wpa_driver_madwifi_disassociate, + .associate = wpa_driver_madwifi_associate, + .set_auth_alg = wpa_driver_madwifi_set_auth_alg, + .set_operstate = wpa_driver_madwifi_set_operstate, + .set_probe_req_ie = wpa_driver_madwifi_set_probe_req_ie, +}; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c new file mode 100644 index 0000000000000..b22109bc7be9d --- /dev/null +++ b/src/drivers/driver_ndis.c @@ -0,0 +1,3126 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifdef __CYGWIN__ +/* Avoid some header file conflicts by not including standard headers for + * cygwin builds when Packet32.h is included. */ +#include "build_config.h" +int close(int fd); +#else /* __CYGWIN__ */ +#include "includes.h" +#endif /* __CYGWIN__ */ +#ifdef CONFIG_USE_NDISUIO +#include <winsock2.h> +#else /* CONFIG_USE_NDISUIO */ +#include <Packet32.h> +#endif /* CONFIG_USE_NDISUIO */ +#ifdef __MINGW32_VERSION +#include <ddk/ntddndis.h> +#else /* __MINGW32_VERSION */ +#include <ntddndis.h> +#endif /* __MINGW32_VERSION */ + +#ifdef _WIN32_WCE +#include <winioctl.h> +#include <nuiouser.h> +#include <devload.h> +#endif /* _WIN32_WCE */ + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "driver_ndis.h" + +int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +static void wpa_driver_ndis_deinit(void *priv); +static void wpa_driver_ndis_poll(void *drv); +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv); +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv); +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv); + + +/* FIX: to be removed once this can be compiled with the complete NDIS + * header files */ +#ifndef OID_802_11_BSSID +#define OID_802_11_BSSID 0x0d010101 +#define OID_802_11_SSID 0x0d010102 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 +#define OID_802_11_ADD_WEP 0x0D010113 +#define OID_802_11_REMOVE_WEP 0x0D010114 +#define OID_802_11_DISASSOCIATE 0x0D010115 +#define OID_802_11_BSSID_LIST 0x0d010217 +#define OID_802_11_AUTHENTICATION_MODE 0x0d010118 +#define OID_802_11_PRIVACY_FILTER 0x0d010119 +#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A +#define OID_802_11_WEP_STATUS 0x0d01011B +#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS +#define OID_802_11_ADD_KEY 0x0d01011D +#define OID_802_11_REMOVE_KEY 0x0d01011E +#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F +#define OID_802_11_TEST 0x0d010120 +#define OID_802_11_CAPABILITY 0x0d010122 +#define OID_802_11_PMKID 0x0d010123 + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +typedef struct NDIS_802_11_SSID { + ULONG SsidLength; + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; +} NDIS_802_11_SSID; + +typedef LONG NDIS_802_11_RSSI; + +typedef enum NDIS_802_11_NETWORK_TYPE { + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11NetworkTypeMax +} NDIS_802_11_NETWORK_TYPE; + +typedef struct NDIS_802_11_CONFIGURATION_FH { + ULONG Length; + ULONG HopPattern; + ULONG HopSet; + ULONG DwellTime; +} NDIS_802_11_CONFIGURATION_FH; + +typedef struct NDIS_802_11_CONFIGURATION { + ULONG Length; + ULONG BeaconPeriod; + ULONG ATIMWindow; + ULONG DSConfig; + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION; + +typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE { + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax +} NDIS_802_11_NETWORK_INFRASTRUCTURE; + +typedef enum NDIS_802_11_AUTHENTICATION_MODE { + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax +} NDIS_802_11_AUTHENTICATION_MODE; + +typedef enum NDIS_802_11_WEP_STATUS { + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS; + +typedef enum NDIS_802_11_PRIVACY_FILTER { + Ndis802_11PrivFilterAcceptAll, + Ndis802_11PrivFilter8021xWEP +} NDIS_802_11_PRIVACY_FILTER; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; + +typedef struct NDIS_WLAN_BSSID_EX { + ULONG Length; + NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */ + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; + ULONG Privacy; + NDIS_802_11_RSSI Rssi; + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX; + +typedef struct NDIS_802_11_BSSID_LIST_EX { + ULONG NumberOfItems; + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX; + +typedef struct NDIS_802_11_FIXED_IEs { + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs; + +typedef struct NDIS_802_11_WEP { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + UCHAR KeyMaterial[1]; +} NDIS_802_11_WEP; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +typedef struct NDIS_802_11_KEY { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; +} NDIS_802_11_KEY; + +typedef struct NDIS_802_11_REMOVE_KEY { + ULONG Length; + ULONG KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY; + +typedef struct NDIS_802_11_AI_REQFI { + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI; + +typedef struct NDIS_802_11_AI_RESFI { + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI; + +typedef struct NDIS_802_11_ASSOCIATION_INFORMATION { + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION; + +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { + NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; + NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; +} NDIS_802_11_AUTHENTICATION_ENCRYPTION; + +typedef struct NDIS_802_11_CAPABILITY { + ULONG Length; + ULONG Version; + ULONG NoOfPMKIDs; + ULONG NoOfAuthEncryptPairsSupported; + NDIS_802_11_AUTHENTICATION_ENCRYPTION + AuthenticationEncryptionSupported[1]; +} NDIS_802_11_CAPABILITY; + +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct BSSID_INFO { + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO; + +typedef struct NDIS_802_11_PMKID { + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID; + +typedef enum NDIS_802_11_STATUS_TYPE { + Ndis802_11StatusType_Authentication, + Ndis802_11StatusType_PMKID_CandidateList = 2, + Ndis802_11StatusTypeMax +} NDIS_802_11_STATUS_TYPE; + +typedef struct NDIS_802_11_STATUS_INDICATION { + NDIS_802_11_STATUS_TYPE StatusType; +} NDIS_802_11_STATUS_INDICATION; + +typedef struct PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { + ULONG Version; + ULONG NumCandidates; + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST; + +typedef struct NDIS_802_11_AUTHENTICATION_REQUEST { + ULONG Length; + NDIS_802_11_MAC_ADDRESS Bssid; + ULONG Flags; +} NDIS_802_11_AUTHENTICATION_REQUEST; + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +#endif /* OID_802_11_BSSID */ + + +#ifndef OID_802_11_PMKID +/* Platform SDK for XP did not include WPA2, so add needed definitions */ + +#define OID_802_11_CAPABILITY 0x0d010122 +#define OID_802_11_PMKID 0x0d010123 + +#define Ndis802_11AuthModeWPA2 6 +#define Ndis802_11AuthModeWPA2PSK 7 + +#define Ndis802_11StatusType_PMKID_CandidateList 2 + +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { + NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; + NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; +} NDIS_802_11_AUTHENTICATION_ENCRYPTION; + +typedef struct NDIS_802_11_CAPABILITY { + ULONG Length; + ULONG Version; + ULONG NoOfPMKIDs; + ULONG NoOfAuthEncryptPairsSupported; + NDIS_802_11_AUTHENTICATION_ENCRYPTION + AuthenticationEncryptionSupported[1]; +} NDIS_802_11_CAPABILITY; + +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct BSSID_INFO { + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO; + +typedef struct NDIS_802_11_PMKID { + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID; + +typedef struct PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { + ULONG Version; + ULONG NumCandidates; + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST; + +#endif /* OID_802_11_CAPABILITY */ + + +#ifdef CONFIG_USE_NDISUIO +#ifndef _WIN32_WCE +#ifdef __MINGW32_VERSION +typedef ULONG NDIS_OID; +#endif /* __MINGW32_VERSION */ +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK + +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISUIO_OPEN_DEVICE \ + _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_QUERY_OID_VALUE \ + _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_SET_OID_VALUE \ + _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_SET_ETHER_TYPE \ + _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_QUERY_BINDING \ + _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_BIND_WAIT \ + _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _NDISUIO_QUERY_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; +} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID; + +typedef struct _NDISUIO_SET_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; +} NDISUIO_SET_OID, *PNDISUIO_SET_OID; + +typedef struct _NDISUIO_QUERY_BINDING +{ + ULONG BindingIndex; + ULONG DeviceNameOffset; + ULONG DeviceNameLength; + ULONG DeviceDescrOffset; + ULONG DeviceDescrLength; +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; +#endif /* _WIN32_WCE */ +#endif /* CONFIG_USE_NDISUIO */ + + +static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + char *data, size_t len) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_OID *o; + size_t buflen = sizeof(*o) + len; + DWORD written; + int ret; + size_t hdrlen; + + o = os_zalloc(buflen); + if (o == NULL) + return -1; + o->Oid = oid; +#ifdef _WIN32_WCE + o->ptcDeviceName = drv->adapter_name; +#endif /* _WIN32_WCE */ + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE, + o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written, + NULL)) { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE " + "failed (oid=%08x): %d", oid, (int) GetLastError()); + os_free(o); + return -1; + } + hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data); + if (written < hdrlen) { + wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); " + "too short", oid, (unsigned int) written); + os_free(o); + return -1; + } + written -= hdrlen; + if (written > len) { + wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > " + "len (%d)",oid, (unsigned int) written, len); + os_free(o); + return -1; + } + os_memcpy(data, o->Data, written); + ret = written; + os_free(o); + return ret; +#else /* CONFIG_USE_NDISUIO */ + char *buf; + PACKET_OID_DATA *o; + int ret; + + buf = os_zalloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + + if (!PacketRequest(drv->adapter, FALSE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + if (o->Length > len) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)", + __func__, oid, (unsigned int) o->Length, len); + os_free(buf); + return -1; + } + os_memcpy(data, o->Data, o->Length); + ret = o->Length; + os_free(buf); + return ret; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + const char *data, size_t len) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_SET_OID *o; + size_t buflen, reallen; + DWORD written; + char txt[50]; + + os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); + wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); + + buflen = sizeof(*o) + len; + reallen = buflen - sizeof(o->Data); + o = os_zalloc(buflen); + if (o == NULL) + return -1; + o->Oid = oid; +#ifdef _WIN32_WCE + o->ptcDeviceName = drv->adapter_name; +#endif /* _WIN32_WCE */ + if (data) + os_memcpy(o->Data, data, len); + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE, + o, reallen, NULL, 0, &written, NULL)) { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE " + "(oid=%08x) failed: %d", oid, (int) GetLastError()); + os_free(o); + return -1; + } + os_free(o); + return 0; +#else /* CONFIG_USE_NDISUIO */ + char *buf; + PACKET_OID_DATA *o; + char txt[50]; + + os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); + wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); + + buf = os_zalloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + if (data) + os_memcpy(o->Data, data, len); + + if (!PacketRequest(drv->adapter, TRUE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + os_free(buf); + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode) +{ + u32 auth_mode = mode; + if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + + +static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv) +{ + u32 auth_mode; + int res; + res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)); + if (res != sizeof(auth_mode)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_AUTHENTICATION_MODE"); + return -1; + } + return auth_mode; +} + + +static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr) +{ + u32 encr_status = encr; + if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr_status, sizeof(encr_status)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_ENCRYPTION_STATUS (%d)", encr); + return -1; + } + return 0; +} + + +static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv) +{ + u32 encr; + int res; + res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr, sizeof(encr)); + if (res != sizeof(encr)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_ENCRYPTION_STATUS"); + return -1; + } + return encr; +} + + +static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndis_data *drv = priv; + + if (drv->wired) { + /* + * Report PAE group address as the "BSSID" for wired + * connection. + */ + 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, (char *) bssid, ETH_ALEN) < + 0 ? -1 : 0; +} + + +static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_SSID buf; + int res; + + res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); + if (res < 4) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID"); + if (drv->wired) { + wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure " + "with a wired interface"); + return 0; + } + return -1; + } + os_memcpy(ssid, buf.Ssid, buf.SsidLength); + return buf.SsidLength; +} + + +static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID buf; + + os_memset(&buf, 0, sizeof(buf)); + buf.SsidLength = ssid_len; + os_memcpy(buf.Ssid, ssid, ssid_len); + /* + * Make sure radio is marked enabled here so that scan request will not + * force SSID to be changed to a random one in order to enable radio at + * that point. + */ + drv->radio_enabled = 1; + return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); +} + + +/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off. + */ +static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) +{ + drv->radio_enabled = 0; + return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4); +} + + +/* Disconnect by setting SSID to random (i.e., likely not used). */ +static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) +{ + char ssid[32]; + int i; + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xff; + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); +} + + +static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static int wpa_driver_ndis_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return 0; +} + + +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ndis_data *drv = priv; + int res; + + if (!drv->radio_enabled) { + wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" + " scan"); + if (wpa_driver_ndis_disconnect(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio"); + } + drv->radio_enabled = 1; + } + + res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( + struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) +{ + struct wpa_scan_res *nr; + u8 *pos; + + if (wpa_scan_get_ie(r, WLAN_EID_SSID)) + return r; /* SSID IE already present */ + + if (ssid->SsidLength == 0 || ssid->SsidLength > 32) + return r; /* No valid SSID inside scan data */ + + nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); + if (nr == NULL) + return r; + + pos = ((u8 *) (nr + 1)) + nr->ie_len; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid->SsidLength; + os_memcpy(pos, ssid->Ssid, ssid->SsidLength); + nr->ie_len += 2 + ssid->SsidLength; + + return nr; +} + + +static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, count, i; + int len; + char *pos; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + return NULL; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + return NULL; + } + count = b->NumberOfItems; + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(b); + return NULL; + } + results->res = os_zalloc(count * sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(b); + return NULL; + } + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < count; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + NDIS_802_11_FIXED_IEs *fixed; + + if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) { + wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d", + (int) bss->IELength); + break; + } + if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) { + /* + * Some NDIS drivers have been reported to include an + * entry with an invalid IELength in scan results and + * this has crashed wpa_supplicant, so validate the + * returned value before using it. + */ + wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan " + "result IE (BSSID=" MACSTR ") IELength=%d", + MAC2STR(bss->MacAddress), + (int) bss->IELength); + break; + } + + r = os_zalloc(sizeof(*r) + bss->IELength - + sizeof(NDIS_802_11_FIXED_IEs)); + if (r == NULL) + break; + + os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN); + r->level = (int) bss->Rssi; + r->freq = bss->Configuration.DSConfig / 1000; + fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs; + r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval); + r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities); + r->tsf = WPA_GET_LE64(fixed->Timestamp); + os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs), + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs)); + r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid); + + results->res[results->num++] = r; + + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + + os_free(b); + + return results; +} + + +static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX index; + int res, res2; + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + if (pairwise) + rkey.KeyIndex |= 1 << 30; + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + if (!pairwise) { + index = key_idx; + res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &index, sizeof(index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return -1; + return 0; +} + + +static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + wep->Length = len; + wep->KeyIndex = key_idx; + if (set_tx) + wep->KeyIndex |= 1 << 31; +#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */ + if (pairwise) + wep->KeyIndex |= 1 << 30; +#endif + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP", + (u8 *) wep, len); + res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + + +static int wpa_driver_ndis_set_key(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) +{ + struct wpa_driver_ndis_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* Group Key */ + pairwise = 0; + if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) + os_memset(bssid, 0xff, ETH_ALEN); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx, + key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + if (set_tx) + nkey->KeyIndex |= 1 << 31; + if (pairwise) + nkey->KeyIndex |= 1 << 30; + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY", + (u8 *) nkey, len); + res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + + +static int +wpa_driver_ndis_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + u32 auth_mode, encr, priv_mode, mode; + + drv->mode = params->mode; + + /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys, + * so static WEP keys needs to be set again after this. */ + if (params->mode == IEEE80211_MODE_IBSS) { + mode = Ndis802_11IBSS; + /* Need to make sure that BSSID polling is enabled for + * IBSS mode. */ + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } else + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->key_mgmt_suite == KEY_MGMT_NONE || + params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + /* Re-set WEP keys if static WEP configuration is used. */ + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int i; + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP " + "key %d", i); + wpa_driver_ndis_set_key(drv, WPA_ALG_WEP, bcast, i, + i == params->wep_tx_keyidx, + NULL, 0, params->wep_key[i], + params->wep_key_len[i]); + } + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & AUTH_ALG_SHARED_KEY) { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; +#ifdef CONFIG_WPS + } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; +#endif /* CONFIG_WPS */ + } else { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: + encr = Ndis802_11EncryptionDisabled; + }; + + if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, + (char *) &priv_mode, sizeof(priv_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_PRIVACY_FILTER (%d)", + (int) priv_mode); + /* Try to continue anyway */ + } + + ndis_set_auth_mode(drv, auth_mode); + ndis_set_encr_status(drv, encr); + + if (params->bssid) { + ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid, + ETH_ALEN); + drv->oid_bssid_set = 1; + } else if (drv->oid_bssid_set) { + ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN); + drv->oid_bssid_set = 0; + } + + return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len); +} + + +static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + + +static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_flush_pmkid(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + int prev_authmode, ret; + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + /* + * Some drivers may refuse OID_802_11_PMKID if authMode is not set to + * WPA2, so change authMode temporarily, if needed. + */ + prev_authmode = ndis_get_auth_mode(drv); + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2); + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (u8 *) &p, 8); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); + + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, prev_authmode); + + return ret; +} + + +static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) +{ + char buf[512], *pos; + NDIS_802_11_ASSOCIATION_INFORMATION *ai; + int len; + union wpa_event_data data; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, i; + + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, + sizeof(buf)); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get association " + "information"); + return -1; + } + if (len > sizeof(buf)) { + /* Some drivers seem to be producing incorrect length for this + * data. Limit the length to the current buffer size to avoid + * crashing in hexdump. The data seems to be otherwise valid, + * so better try to use it. */ + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association " + "information length %d", len); + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, + buf, sizeof(buf)); + if (len < -1) { + wpa_printf(MSG_DEBUG, "NDIS: re-reading association " + "information failed"); + return -1; + } + if (len > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association" + " information length %d (re-read)", len); + len = sizeof(buf); + } + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: association information", + (u8 *) buf, len); + if (len < sizeof(*ai)) { + wpa_printf(MSG_DEBUG, "NDIS: too short association " + "information"); + return -1; + } + ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d " + "off_resp=%d len_req=%d len_resp=%d", + ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs, + (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs, + (int) ai->RequestIELength, (int) ai->ResponseIELength); + + if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len || + ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) { + wpa_printf(MSG_DEBUG, "NDIS: association information - " + "IE overflow"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs", + (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs", + (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength); + + os_memset(&data, 0, sizeof(data)); + data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs; + data.assoc_info.req_ies_len = ai->RequestIELength; + data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs; + data.assoc_info.resp_ies_len = ai->ResponseIELength; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + goto skip_scan_results; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + b = NULL; + goto skip_scan_results; + } + wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo", + (unsigned int) b->NumberOfItems); + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < b->NumberOfItems; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 && + bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) { + data.assoc_info.beacon_ies = + ((u8 *) bss->IEs) + + sizeof(NDIS_802_11_FIXED_IEs); + data.assoc_info.beacon_ies_len = + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs", + data.assoc_info.beacon_ies, + data.assoc_info.beacon_ies_len); + break; + } + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + +skip_scan_results: + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(b); + + return 0; +} + + +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + int poll; + + if (drv->wired) + return; + + if (wpa_driver_ndis_get_bssid(drv, bssid)) { + /* Disconnected */ + if (!is_zero_ether_addr(drv->bssid)) { + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } else { + /* Connected */ + if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) { + os_memcpy(drv->bssid, bssid, ETH_ALEN); + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + } + + /* When using integrated NDIS event receiver, we can skip BSSID + * polling when using infrastructure network. However, when using + * IBSS mode, many driver do not seem to generate connection event, + * so we need to enable BSSID polling to figure out when IBSS network + * has been formed. + */ + poll = drv->mode == IEEE80211_MODE_IBSS; +#ifndef CONFIG_NDIS_EVENTS_INTEGRATED +#ifndef _WIN32_WCE + poll = 1; +#endif /* _WIN32_WCE */ +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + + if (poll) { + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } +} + + +static void wpa_driver_ndis_poll(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_poll_timeout(drv, NULL); +} + + +/* Called when driver generates Media Connect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */ +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event"); + if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) { + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } +} + + +/* Called when driver generates Media Disconnect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */ +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event"); + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_AUTHENTICATION_REQUEST *req; + int pairwise = 0, group = 0; + union wpa_event_data event; + + if (data_len < sizeof(*req)) { + wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request " + "Event (len=%d)", data_len); + return; + } + req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data; + + wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: " + "Bssid " MACSTR " Flags 0x%x", + MAC2STR(req->Bssid), (int) req->Flags); + + if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) == + NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) + pairwise = 1; + else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) == + NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) + group = 1; + + if (pairwise || group) { + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = pairwise; + wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + } +} + + +static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List " + "Event (len=%d)", data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d " + "NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List " + "Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow"); + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x", + i, MAC2STR(p->BSSID), (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + + +/* Called when driver calls NdisMIndicateStatus() with + * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */ +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_STATUS_INDICATION *status; + + if (data == NULL || data_len < sizeof(*status)) + return; + + wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication", + data, data_len); + + status = (NDIS_802_11_STATUS_INDICATION *) data; + data += sizeof(status); + data_len -= sizeof(status); + + switch (status->StatusType) { + case Ndis802_11StatusType_Authentication: + wpa_driver_ndis_event_auth(drv, data, data_len); + break; + case Ndis802_11StatusType_PMKID_CandidateList: + wpa_driver_ndis_event_pmkid(drv, data, data_len); + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d", + (int) status->StatusType); + break; + } +} + + +/* Called when an adapter is added */ +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + int i; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival"); + + for (i = 0; i < 30; i++) { + /* Re-open Packet32/NDISUIO connection */ + wpa_driver_ndis_adapter_close(drv); + if (wpa_driver_ndis_adapter_init(drv) < 0 || + wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization " + "(%d) failed", i); + os_sleep(1, 0); + } else { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized"); + break; + } + } + + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +/* Called when an adapter is removed */ +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal"); + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static void +wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability"); + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) { + wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) { + wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management " + "supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) { + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) { + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + } + + ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled); + + /* Could also verify OID_802_11_ADD_KEY error reporting and + * support for OID_802_11_ASSOCIATION_INFORMATION. */ + + if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA && + drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA"); + drv->has_capability = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: no WPA support found"); + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv) +{ + char buf[512]; + int len; + size_t i; + NDIS_802_11_CAPABILITY *c; + + drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE; + + len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf)); + if (len < 0) { + wpa_driver_ndis_get_wpa_capability(drv); + return; + } + + wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len); + c = (NDIS_802_11_CAPABILITY *) buf; + if (len < sizeof(*c) || c->Version != 2) { + wpa_printf(MSG_DEBUG, "NDIS: unsupported " + "OID_802_11_CAPABILITY data"); + return; + } + wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - " + "NoOfPMKIDs %d NoOfAuthEncrPairs %d", + (int) c->NoOfPMKIDs, + (int) c->NoOfAuthEncryptPairsSupported); + drv->has_capability = 1; + drv->no_of_pmkid = c->NoOfPMKIDs; + for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) { + NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae; + ae = &c->AuthenticationEncryptionSupported[i]; + if ((char *) (ae + 1) > buf + len) { + wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list " + "overflow"); + break; + } + wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d", + i, (int) ae->AuthModeSupported, + (int) ae->EncryptStatusSupported); + switch (ae->AuthModeSupported) { + case Ndis802_11AuthModeOpen: + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + break; + case Ndis802_11AuthModeShared: + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + break; + case Ndis802_11AuthModeWPA: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + break; + case Ndis802_11AuthModeWPAPSK: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + break; + case Ndis802_11AuthModeWPA2: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case Ndis802_11AuthModeWPA2PSK: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case Ndis802_11AuthModeWPANone: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE; + break; + default: + break; + } + switch (ae->EncryptStatusSupported) { + case Ndis802_11Encryption1Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case Ndis802_11Encryption2Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case Ndis802_11Encryption3Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + default: + break; + } + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndis_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static const char * wpa_driver_ndis_get_ifname(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->ifname; +} + + +static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->own_addr; +} + + +#ifdef _WIN32_WCE + +#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512) + +static void ndisuio_notification_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + NDISUIO_DEVICE_NOTIFICATION *hdr; + u8 buf[NDISUIO_MSG_SIZE]; + DWORD len, flags; + + if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0, + &flags)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "ReadMsgQueue failed: %d", (int) GetLastError()); + return; + } + + if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "Too short message (len=%d)", (int) len); + return; + } + + hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x", + (int) len, hdr->dwNotificationType); + + switch (hdr->dwNotificationType) { +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL"); + wpa_driver_ndis_event_adapter_arrival(drv); + break; +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL"); + wpa_driver_ndis_event_adapter_removal(drv); + break; +#endif + case NDISUIO_NOTIFICATION_MEDIA_CONNECT: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT"); + SetEvent(drv->connected_event); + wpa_driver_ndis_event_connect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT: + ResetEvent(drv->connected_event); + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT"); + wpa_driver_ndis_event_disconnect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION"); +#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420 + wpa_driver_ndis_event_media_specific( + drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize); +#else + wpa_driver_ndis_event_media_specific( + drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer, + (size_t) hdr->uiStatusBufferSize); +#endif + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x", + hdr->dwNotificationType); + break; + } +} + + +static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv) +{ + NDISUIO_REQUEST_NOTIFICATION req; + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = 0; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION, + NULL, 0, NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (drv->event_queue) { + eloop_unregister_event(drv->event_queue, + sizeof(drv->event_queue)); + CloseHandle(drv->event_queue); + drv->event_queue = NULL; + } + + if (drv->connected_event) { + CloseHandle(drv->connected_event); + drv->connected_event = NULL; + } +} + + +static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv) +{ + MSGQUEUEOPTIONS opt; + NDISUIO_REQUEST_NOTIFICATION req; + + drv->connected_event = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + if (drv->connected_event == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateEvent failed: %d", + (int) GetLastError()); + return -1; + } + + memset(&opt, 0, sizeof(opt)); + opt.dwSize = sizeof(opt); + opt.dwMaxMessages = 5; + opt.cbMaxMessage = NDISUIO_MSG_SIZE; + opt.bReadAccess = TRUE; + + drv->event_queue = CreateMsgQueue(NULL, &opt); + if (drv->event_queue == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateMsgQueue failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL | +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + NDISUIO_NOTIFICATION_ADAPTER_REMOVAL | +#endif + NDISUIO_NOTIFICATION_MEDIA_CONNECT | + NDISUIO_NOTIFICATION_MEDIA_DISCONNECT | + NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + eloop_register_event(drv->event_queue, sizeof(drv->event_queue), + ndisuio_notification_receive, drv, NULL); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error, found = 0; + DWORD written; + char name[256], desc[256], *dpos; + WCHAR *pos; + size_t j, len, dlen; + + b = os_malloc(blen); + if (b == NULL) + return -1; + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + if (os_strstr(name, drv->ifname)) { + wpa_printf(MSG_DEBUG, "NDIS: Interface name match"); + found = 1; + break; + } + + if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0) + { + wpa_printf(MSG_DEBUG, "NDIS: Interface description " + "match"); + found = 1; + break; + } + } + + if (!found) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(b); + return -1; + } + + os_strlcpy(drv->ifname, + os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name, + sizeof(drv->ifname)); +#ifdef _WIN32_WCE + drv->adapter_name = wpa_strdup_tchar(drv->ifname); + if (drv->adapter_name == NULL) { + wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for " + "adapter name"); + os_free(b); + return -1; + } +#endif /* _WIN32_WCE */ + + dpos = os_strstr(desc, " - "); + if (dpos) + dlen = dpos - desc; + else + dlen = os_strlen(desc); + drv->adapter_desc = os_malloc(dlen + 1); + if (drv->adapter_desc) { + os_memcpy(drv->adapter_desc, desc, dlen); + drv->adapter_desc[dlen] = '\0'; + } + + os_free(b); + + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; +#define MAX_ADAPTERS 32 + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i, found_name, found_desc; + size_t dlen; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return -1; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return -1; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return -1; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return -1; + } + + found_name = found_desc = -1; + for (i = 0; i < num_name; i++) { + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", + i, name[i], desc[i]); + if (found_name == -1 && os_strstr(name[i], drv->ifname)) + found_name = i; + if (found_desc == -1 && + os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) == + 0) + found_desc = i; + } + + if (found_name < 0 && found_desc >= 0) { + wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on " + "description '%s'", + name[found_desc], desc[found_desc]); + found_name = found_desc; + os_strlcpy(drv->ifname, + os_strncmp(name[found_desc], "\\Device\\NPF_", 12) + == 0 ? name[found_desc] + 12 : name[found_desc], + sizeof(drv->ifname)); + } + + if (found_name < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(names); + return -1; + } + + i = found_name; + pos = os_strrchr(desc[i], '('); + if (pos) { + dlen = pos - desc[i]; + pos--; + if (pos > desc[i] && *pos == ' ') + dlen--; + } else { + dlen = os_strlen(desc[i]); + } + drv->adapter_desc = os_malloc(dlen + 1); + if (drv->adapter_desc) { + os_memcpy(drv->adapter_desc, desc[i], dlen); + drv->adapter_desc[dlen] = '\0'; + } + + os_free(names); + + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__) +#ifndef _WIN32_WCE +/* + * These structures are undocumented for WinXP; only WinCE version is + * documented. These would be included wzcsapi.h if it were available. Some + * changes here have been needed to make the structures match with WinXP SP2. + * It is unclear whether these work with any other version. + */ + +typedef struct { + LPWSTR wszGuid; +} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY; + +typedef struct { + DWORD dwNumIntfs; + PINTF_KEY_ENTRY pIntfs; +} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE; + +typedef struct { + DWORD dwDataLen; + LPBYTE pData; +} RAW_DATA, *PRAW_DATA; + +typedef struct { + LPWSTR wszGuid; + LPWSTR wszDescr; + ULONG ulMediaState; + ULONG ulMediaType; + ULONG ulPhysicalMediaType; + INT nInfraMode; + INT nAuthMode; + INT nWepStatus; +#ifndef _WIN32_WCE + u8 pad[2]; /* why is this needed? */ +#endif /* _WIN32_WCE */ + DWORD dwCtlFlags; + DWORD dwCapabilities; /* something added for WinXP SP2(?) */ + RAW_DATA rdSSID; + RAW_DATA rdBSSID; + RAW_DATA rdBSSIDList; + RAW_DATA rdStSSIDList; + RAW_DATA rdCtrlData; +#ifdef UNDER_CE + BOOL bInitialized; +#endif + DWORD nWPAMCastCipher; + /* add some extra buffer for later additions since this interface is + * far from stable */ + u8 later_additions[100]; +} INTF_ENTRY, *PINTF_ENTRY; + +#define INTF_ALL 0xffffffff +#define INTF_ALL_FLAGS 0x0000ffff +#define INTF_CTLFLAGS 0x00000010 +#define INTFCTL_ENABLED 0x8000 +#endif /* _WIN32_WCE */ + + +#ifdef _WIN32_WCE +static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv) +{ + HANDLE ndis; + TCHAR multi[100]; + int len; + + len = _tcslen(drv->adapter_name); + if (len > 80) + return -1; + + ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (ndis == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS " + "device: %d", (int) GetLastError()); + return -1; + } + + len++; + memcpy(multi, drv->adapter_name, len * sizeof(TCHAR)); + memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR)); + len += 9; + + if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER, + multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL)) + { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER " + "failed: 0x%x", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz", + (u8 *) multi, len * sizeof(TCHAR)); + CloseHandle(ndis); + return -1; + } + + CloseHandle(ndis); + + wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO " + "protocol"); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ +#ifdef _WIN32_WCE + HKEY hk, hk2; + LONG ret; + DWORD i, hnd, len; + TCHAR keyname[256], devname[256]; + +#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig") + + if (enable) { + HANDLE h; + h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL); + if (h == INVALID_HANDLE_VALUE || h == 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC " + "- ActivateDeviceEx failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled"); + return wpa_driver_ndis_rebind_adapter(drv); + } + + /* + * Unfortunately, just disabling the WZC for an interface is not enough + * to free NDISUIO for us, so need to disable and unload WZC completely + * for now when using WinCE with NDISUIO. In addition, must request + * NDISUIO protocol to be rebound to the adapter in order to free the + * NDISUIO binding that WZC hold before us. + */ + + /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) " + "failed: %d %d", (int) ret, (int) GetLastError()); + return -1; + } + + for (i = 0; ; i++) { + len = sizeof(keyname); + ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL, + NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find active " + "WZC - assuming it is not running."); + RegCloseKey(hk); + return -1; + } + + ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) " + "failed: %d %d", + (int) ret, (int) GetLastError()); + continue; + } + + len = sizeof(devname); + ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL, + (LPBYTE) devname, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(" + "DEVKEY_VALNAME) failed: %d %d", + (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + continue; + } + + if (_tcscmp(devname, WZC_DRIVER) == 0) + break; + + RegCloseKey(hk2); + } + + RegCloseKey(hk); + + /* Found WZC - get handle to it. */ + len = sizeof(hnd); + ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL, + (PUCHAR) &hnd, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) " + "failed: %d %d", (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + return -1; + } + + RegCloseKey(hk2); + + /* Deactivate WZC */ + if (!DeactivateDevice((HANDLE) hnd)) { + wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily"); + drv->wzc_disabled = 1; + return wpa_driver_ndis_rebind_adapter(drv); + +#else /* _WIN32_WCE */ + + HMODULE hm; + DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr, + PINTFS_KEY_TABLE pIntfs); + DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, + LPDWORD pdwOutFlags); + DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, LPDWORD pdwOutFlags); + int ret = -1, j; + DWORD res; + INTFS_KEY_TABLE guids; + INTF_ENTRY intf; + char guid[128]; + WCHAR *pos; + DWORD flags, i; + + hm = LoadLibrary(TEXT("wzcsapi.dll")); + if (hm == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) " + "- WZC probably not running", + (unsigned int) GetLastError()); + return -1; + } + +#ifdef _WIN32_WCE + wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface"); +#else /* _WIN32_WCE */ + wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface"); +#endif /* _WIN32_WCE */ + + if (wzc_enum_interf == NULL || wzc_query_interf == NULL || + wzc_set_interf == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, " + "WZCQueryInterface, or WZCSetInterface not found " + "in wzcsapi.dll"); + goto fail; + } + + os_memset(&guids, 0, sizeof(guids)); + res = wzc_enum_interf(NULL, &guids); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; " + "WZC service is apparently not running", + (int) res); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces", + (int) guids.dwNumIntfs); + + for (i = 0; i < guids.dwNumIntfs; i++) { + pos = guids.pIntfs[i].wszGuid; + for (j = 0; j < sizeof(guid); j++) { + guid[j] = (char) *pos; + if (*pos == 0) + break; + pos++; + } + guid[sizeof(guid) - 1] = '\0'; + wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'", + (int) i, guid); + if (os_strstr(drv->ifname, guid) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "NDIS: Current interface found from " + "WZC"); + break; + } + + if (i >= guids.dwNumIntfs) { + wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from " + "WZC"); + goto fail; + } + + os_memset(&intf, 0, sizeof(intf)); + intf.wszGuid = guids.pIntfs[i].wszGuid; + /* Set flags to verify that the structure has not changed. */ + intf.dwCtlFlags = -1; + flags = 0; + res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the " + "WZC interface: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x", + (int) flags, (int) intf.dwCtlFlags); + + if (intf.dwCtlFlags == -1) { + wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed " + "again - could not disable WZC"); + wpa_hexdump(MSG_MSGDUMP, "NDIS: intf", + (u8 *) &intf, sizeof(intf)); + goto fail; + } + + if (enable) { + if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) { + wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this " + "interface"); + intf.dwCtlFlags |= INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to enable " + "WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this " + "interface"); + drv->wzc_disabled = 0; + } + } else { + if (intf.dwCtlFlags & INTFCTL_ENABLED) { + wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this " + "interface"); + intf.dwCtlFlags &= ~INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to " + "disable WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily " + "for this interface"); + drv->wzc_disabled = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for " + "this interface"); + } + } + + ret = 0; + +fail: + FreeLibrary(hm); + + return ret; +#endif /* _WIN32_WCE */ +} + +#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + + +#ifdef CONFIG_USE_NDISUIO +/* + * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able + * to export this handle. This is somewhat ugly, but there is no better + * mechanism available to pass data from driver interface to l2_packet wrapper. + */ +static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + +HANDLE driver_ndis_get_ndisuio_handle(void) +{ + return driver_ndis_ndisuio_handle; +} +#endif /* CONFIG_USE_NDISUIO */ + + +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO +#ifndef _WIN32_WCE +#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio") + DWORD written; +#endif /* _WIN32_WCE */ + drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (drv->ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return -1; + } + driver_ndis_ndisuio_handle = drv->ndisuio; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } +#endif /* _WIN32_WCE */ + + return 0; +#else /* CONFIG_USE_NDISUIO */ + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + DWORD written; +#define MAX_NDIS_DEVICE_NAME_LEN 256 + WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN]; + size_t len, i, pos; + const char *prefix = "\\DEVICE\\"; + +#ifdef _WIN32_WCE + pos = 0; +#else /* _WIN32_WCE */ + pos = 8; +#endif /* _WIN32_WCE */ + len = pos + os_strlen(drv->ifname); + if (len >= MAX_NDIS_DEVICE_NAME_LEN) + return -1; + for (i = 0; i < pos; i++) + ifname[i] = (WCHAR) prefix[i]; + for (i = pos; i < len; i++) + ifname[i] = (WCHAR) drv->ifname[i - pos]; + ifname[i] = L'\0'; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE, + ifname, len * sizeof(WCHAR), NULL, 0, &written, + NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE " + "failed: %d", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname", + (const u8 *) ifname, len * sizeof(WCHAR)); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully"); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname); + drv->adapter = PacketOpenAdapter(ifname); + if (drv->adapter == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for " + "'%s'", ifname); + return -1; + } + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + if (drv->ndisuio != INVALID_HANDLE_VALUE) + CloseHandle(drv->ndisuio); +#else /* CONFIG_USE_NDISUIO */ + if (drv->adapter) + PacketCloseAdapter(drv->adapter); +#endif /* CONFIG_USE_NDISUIO */ +} + + +static void * wpa_driver_ndis_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndis_data *drv; + u32 mode; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + /* + * Compatibility code to strip possible prefix from the GUID. Previous + * versions include \Device\NPF_ prefix for all names, but the internal + * interface name is now only the GUI. Both Packet32 and NDISUIO + * prefixes are supported. + */ + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + ifname += 12; + else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0) + ifname += 8; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (wpa_driver_ndis_adapter_init(drv) < 0) { + os_free(drv); + return NULL; + } + + if (wpa_driver_ndis_get_names(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + wpa_driver_ndis_set_wzc(drv, 0); + + if (wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS, + (char *) drv->own_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS " + "failed"); + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + wpa_driver_ndis_get_capability(drv); + + /* Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_ndis_flush_pmkid(drv); + + /* + * Disconnect to make sure that driver re-associates if it was + * connected. + */ + wpa_driver_ndis_disconnect(drv); + + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail, + drv->ifname, drv->adapter_desc); + if (drv->events == NULL) { + wpa_driver_ndis_deinit(drv); + return NULL; + } + eloop_register_event(drv->event_avail, sizeof(drv->event_avail), + wpa_driver_ndis_event_pipe_cb, drv, NULL); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + if (ndisuio_notification_init(drv) < 0) { + wpa_driver_ndis_deinit(drv); + return NULL; + } +#endif /* _WIN32_WCE */ + + /* Set mode here in case card was configured for ad-hoc mode + * previously. */ + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "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; +} + + +static void wpa_driver_ndis_deinit(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + if (drv->events) { + eloop_unregister_event(drv->event_avail, + sizeof(drv->event_avail)); + ndis_events_deinit(drv->events); + } +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + ndisuio_notification_deinit(drv); +#endif /* _WIN32_WCE */ + + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_flush_pmkid(drv); + wpa_driver_ndis_disconnect(drv); + if (wpa_driver_ndis_radio_off(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn " + "radio off"); + } + + wpa_driver_ndis_adapter_close(drv); + + if (drv->wzc_disabled) + wpa_driver_ndis_set_wzc(drv, 1); + +#ifdef _WIN32_WCE + os_free(drv->adapter_name); +#endif /* _WIN32_WCE */ + os_free(drv->adapter_desc); + os_free(drv); +} + + +static struct wpa_interface_info * +wpa_driver_ndis_get_interfaces(void *global_priv) +{ + struct wpa_interface_info *iface = NULL, *niface; + +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + HANDLE ndisuio; + + ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return NULL; + } + +#ifndef _WIN32_WCE + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(ndisuio); + return NULL; + } +#endif /* _WIN32_WCE */ + + b = os_malloc(blen); + if (b == NULL) { + CloseHandle(ndisuio); + return NULL; + } + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name, "\\DEVICE\\", 8) == 0) + niface->ifname = os_strdup(name + 8); + else + niface->ifname = os_strdup(name); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc); + niface->next = iface; + iface = niface; + } + + os_free(b); + CloseHandle(ndisuio); +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return NULL; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return NULL; + } + + for (i = 0; i < num_name; i++) { + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0) + niface->ifname = os_strdup(name[i] + 12); + else + niface->ifname = os_strdup(name[i]); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc[i]); + niface->next = iface; + iface = niface; + } + +#endif /* CONFIG_USE_NDISUIO */ + + return iface; +} + + +const struct wpa_driver_ops wpa_driver_ndis_ops = { + "ndis", + "Windows NDIS driver", + wpa_driver_ndis_get_bssid, + wpa_driver_ndis_get_ssid, + wpa_driver_ndis_set_wpa, + wpa_driver_ndis_set_key, + wpa_driver_ndis_init, + wpa_driver_ndis_deinit, + NULL /* set_param */, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_ndis_scan, + NULL /* get_scan_results */, + wpa_driver_ndis_deauthenticate, + wpa_driver_ndis_disassociate, + wpa_driver_ndis_associate, + NULL /* set_auth_alg */, + wpa_driver_ndis_add_pmkid, + wpa_driver_ndis_remove_pmkid, + wpa_driver_ndis_flush_pmkid, + wpa_driver_ndis_get_capa, + wpa_driver_ndis_poll, + wpa_driver_ndis_get_ifname, + wpa_driver_ndis_get_mac_addr, + NULL /* send_eapol */, + NULL /* set_operstate */, + NULL /* mlme_setprotection */, + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_ndis_get_scan_results, + NULL /* set_probe_req_ie */, + NULL /* set_mode */, + NULL /* set_country */, + NULL /* global_init */, + NULL /* global_deinit */, + NULL /* init2 */, + wpa_driver_ndis_get_interfaces +}; diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h new file mode 100644 index 0000000000000..cdce4bac85abd --- /dev/null +++ b/src/drivers/driver_ndis.h @@ -0,0 +1,64 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DRIVER_NDIS_H +#define DRIVER_NDIS_H + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED +struct ndis_events_data; +struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event, + const char *ifname, + const char *desc); +void ndis_events_deinit(struct ndis_events_data *events); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +struct wpa_driver_ndis_data { + void *ctx; + char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */ +#ifdef _WIN32_WCE + TCHAR *adapter_name; + HANDLE event_queue; /* NDISUIO notifier MsgQueue */ + HANDLE connected_event; /* WpaSupplicantConnected event */ +#endif /* _WIN32_WCE */ + u8 own_addr[ETH_ALEN]; +#ifdef CONFIG_USE_NDISUIO + HANDLE ndisuio; +#else /* CONFIG_USE_NDISUIO */ + LPADAPTER adapter; +#endif /* CONFIG_USE_NDISUIO */ + u8 bssid[ETH_ALEN]; + + int has_capability; + int no_of_pmkid; + int radio_enabled; + struct wpa_driver_capa capa; + struct ndis_pmkid_entry *pmkid; + char *adapter_desc; + int wired; + int mode; + int wzc_disabled; + int oid_bssid_set; +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + HANDLE events_pipe, event_avail; + struct ndis_events_data *events; +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ +}; + +#endif /* DRIVER_NDIS_H */ diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c new file mode 100644 index 0000000000000..4bee9aa543eb5 --- /dev/null +++ b/src/drivers/driver_ndis_.c @@ -0,0 +1,105 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface - event processing + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "driver.h" +#include "eloop.h" + +/* Keep this event processing in a separate file and without WinPcap headers to + * avoid conflicts with some of the header files. */ +struct _ADAPTER; +typedef struct _ADAPTER * LPADAPTER; +#include "driver_ndis.h" + + +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len); +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv); + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, + EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, + EVENT_ADAPTER_REMOVAL }; + +/* Event data: + * enum event_types (as int, i.e., 4 octets) + * data length (2 octets (big endian), optional) + * data (variable len, optional) + */ + + +static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv, + u8 *buf, size_t len) +{ + u8 *pos, *data = NULL; + enum event_types type; + size_t data_len = 0; + + wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len); + if (len < sizeof(int)) + return; + type = *((int *) buf); + pos = buf + sizeof(int); + wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); + + if (buf + len - pos > 2) { + data_len = (int) *pos++ << 8; + data_len += *pos++; + if (data_len > (size_t) (buf + len - pos)) { + wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); + return; + } + data = pos; + wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); + } + + switch (type) { + case EVENT_CONNECT: + wpa_driver_ndis_event_connect(drv); + break; + case EVENT_DISCONNECT: + wpa_driver_ndis_event_disconnect(drv); + break; + case EVENT_MEDIA_SPECIFIC: + wpa_driver_ndis_event_media_specific(drv, data, data_len); + break; + case EVENT_ADAPTER_ARRIVAL: + wpa_driver_ndis_event_adapter_arrival(drv); + break; + case EVENT_ADAPTER_REMOVAL: + wpa_driver_ndis_event_adapter_removal(drv); + break; + } +} + + +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + u8 buf[512]; + DWORD len; + + ResetEvent(drv->event_avail); + if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL)) + wpa_driver_ndis_event_process(drv, buf, len); + else { + wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__, + (int) GetLastError()); + } +} diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c new file mode 100644 index 0000000000000..d8bd72e7021a9 --- /dev/null +++ b/src/drivers/driver_ndiswrapper.c @@ -0,0 +1,366 @@ +/* + * WPA Supplicant - driver interaction with Linux ndiswrapper + * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu> + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ndiswrapper_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +struct wpa_key +{ + wpa_alg alg; + const u8 *addr; + int key_index; + int set_tx; + const u8 *seq; + size_t seq_len; + const u8 *key; + size_t key_len; +}; + +struct wpa_assoc_info +{ + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int freq; + const u8 *wpa_ie; + size_t wpa_ie_len; + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + int auth_alg; + int mode; +}; + +#define PRIV_RESET SIOCIWFIRSTPRIV+0 +#define WPA_SET_WPA SIOCIWFIRSTPRIV+1 +#define WPA_SET_KEY SIOCIWFIRSTPRIV+2 +#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3 +#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4 +#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5 +#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6 +#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7 +#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8 +#define WPA_INIT SIOCIWFIRSTPRIV+9 +#define WPA_DEINIT SIOCIWFIRSTPRIV+10 +#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 + +static int get_socket(void) +{ + static const int families[] = { + AF_INET, AF_IPX, AF_AX25, AF_APPLETALK + }; + unsigned int i; + int sock; + + for (i = 0; i < sizeof(families) / sizeof(int); ++i) { + sock = socket(families[i], SOCK_DGRAM, 0); + if (sock >= 0) + return sock; + } + + return -1; +} + +static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request, + struct iwreq *pwrq) +{ + os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ); + return ioctl(drv->sock, request, pwrq); +} + +static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct iwreq priv_req; + int ret = 0; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.flags = enabled; + if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_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) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct wpa_key wpa_key; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + wpa_key.alg = alg; + wpa_key.addr = addr; + wpa_key.key_index = key_idx; + wpa_key.set_tx = set_tx; + wpa_key.seq = seq; + wpa_key.seq_len = seq_len; + wpa_key.key = key; + wpa_key.key_len = key_len; + + priv_req.u.data.pointer = (void *)&wpa_key; + priv_req.u.data.length = sizeof(wpa_key); + + if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0) + ret = -1; + + if (alg == WPA_ALG_NONE) { + /* + * ndiswrapper did not seem to be clearing keys properly in + * some cases with WPA_SET_KEY. For example, roaming from WPA + * enabled AP to plaintext one seemed to fail since the driver + * did not associate. Try to make sure the keys are cleared so + * that plaintext APs can be used in all cases. + */ + wpa_driver_wext_set_key(drv->wext, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + } + + return ret; +} + +static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0) + ret = -1; + + return ret; +} + +static int wpa_ndiswrapper_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = reason_code; + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int +wpa_ndiswrapper_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct wpa_assoc_info wpa_assoc_info; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); + + wpa_assoc_info.bssid = params->bssid; + wpa_assoc_info.ssid = params->ssid; + wpa_assoc_info.ssid_len = params->ssid_len; + wpa_assoc_info.freq = params->freq; + wpa_assoc_info.wpa_ie = params->wpa_ie; + wpa_assoc_info.wpa_ie_len = params->wpa_ie_len; + wpa_assoc_info.pairwise_suite = params->pairwise_suite; + wpa_assoc_info.group_suite = params->group_suite; + wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite; + wpa_assoc_info.auth_alg = params->auth_alg; + wpa_assoc_info.mode = params->mode; + + priv_req.u.data.pointer = (void *)&wpa_assoc_info; + priv_req.u.data.length = sizeof(wpa_assoc_info); + + if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = auth_alg; + if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_ndiswrapper_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.pointer = (void *) capa; + priv_req.u.data.length = sizeof(*capa); + if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0) + ret = -1; + return ret; + +} + + +static int wpa_ndiswrapper_set_operstate(void *priv, int state) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndiswrapper_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = get_socket(); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_ndiswrapper_deinit(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { + .name = "ndiswrapper", + .desc = "Linux ndiswrapper", + .set_wpa = wpa_ndiswrapper_set_wpa, + .set_key = wpa_ndiswrapper_set_key, + .set_countermeasures = wpa_ndiswrapper_set_countermeasures, + .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, + .deauthenticate = wpa_ndiswrapper_deauthenticate, + .disassociate = wpa_ndiswrapper_disassociate, + .associate = wpa_ndiswrapper_associate, + .set_auth_alg = wpa_ndiswrapper_set_auth_alg, + + .get_bssid = wpa_ndiswrapper_get_bssid, + .get_ssid = wpa_ndiswrapper_get_ssid, + .scan = wpa_ndiswrapper_scan, + .get_scan_results2 = wpa_ndiswrapper_get_scan_results, + .init = wpa_ndiswrapper_init, + .deinit = wpa_ndiswrapper_deinit, + .get_capa = wpa_ndiswrapper_get_capa, + .set_operstate = wpa_ndiswrapper_set_operstate, +}; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c new file mode 100644 index 0000000000000..ec26480d625bc --- /dev/null +++ b/src/drivers/driver_nl80211.c @@ -0,0 +1,2752 @@ +/* + * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include "nl80211_copy.h" +#ifdef CONFIG_CLIENT_MLME +#include <netpacket/packet.h> +#include <linux/if_ether.h> +#include "radiotap.h" +#include "radiotap_iter.h" +#endif /* CONFIG_CLIENT_MLME */ + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IF_OPER_DORMANT +#define IF_OPER_DORMANT 5 +#endif +#ifndef IF_OPER_UP +#define IF_OPER_UP 6 +#endif + + +struct wpa_driver_nl80211_data { + void *ctx; + int wext_event_sock; + int ioctl_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; + + struct nl_handle *nl_handle; + struct nl_cache *nl_cache; + struct nl_cb *nl_cb; + struct genl_family *nl80211; + +#ifdef CONFIG_CLIENT_MLME + int monitor_sock; /* socket for monitor */ + int monitor_ifidx; +#endif /* CONFIG_CLIENT_MLME */ +}; + + +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, + void *timeout_ctx); +static int wpa_driver_nl80211_set_mode(void *priv, int mode); +static int wpa_driver_nl80211_flush_pmkid(void *priv); +static int wpa_driver_nl80211_get_range(void *priv); +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); + + +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + + +struct family_data { + const char *group; + int id; +}; + + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_data *res = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + }; + + return NL_SKIP; +} + + +static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs(drv, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_oper_ifla( + struct wpa_driver_nl80211_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +static int wpa_driver_nl80211_set_auth_param( + struct wpa_driver_nl80211_data *drv, int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_freq(void *priv, int freq) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_nl80211_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_pmkidcand( + struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocreqie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocrespie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_nl80211_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + wpa_driver_nl80211_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_nl80211_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case IWEVASSOCREQIE: + wpa_driver_nl80211_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + wpa_driver_nl80211_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + wpa_driver_nl80211_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_nl80211_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_nl80211_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int process_event(struct nl_msg *msg, void *arg) +{ + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + if (ifindex != drv->ifindex) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" + " for foreign interface (ifindex %d)", + gnlh->cmd, ifindex); + return NL_SKIP; + } + } + + switch (gnlh->cmd) { + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); + /* + * Need to indicate that scan results are available in order + * not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + default: + wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", + gnlh->cmd); + break; + } + + return NL_SKIP; +} + + +static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct nl_cb *cb; + struct wpa_driver_nl80211_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + return; + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); + nl_recvmsgs(drv->nl_handle, cb); + nl_cb_put(cb); +} + + +static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, + int *flags) +{ + return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_nl80211_set_ifflags_ifname( + struct wpa_driver_nl80211_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, + int flags) +{ + return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain + * @priv: driver_nl80211 private data + * @alpha2_arg: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This asks nl80211 to set the regulatory domain for given + * country ISO / IEC alpha2. + */ +static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +{ + struct wpa_driver_nl80211_data *drv = priv; + char alpha2[3]; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + alpha2[0] = alpha2_arg[0]; + alpha2[1] = alpha2_arg[1]; + alpha2[2] = '\0'; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + if (send_and_recv_msgs(drv, msg, NULL, NULL)) + return -EINVAL; + return 0; +nla_put_failure: + return -EINVAL; +} + + +static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_MGMT_EXTRA_IE, 0); + + NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */); + if (ies) + NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + + +#ifdef CONFIG_CLIENT_MLME + +static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, + int drop_unencrypted, int userspace_mlme) +{ +#ifdef NL80211_CMD_SET_VIF + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_VIF, 0); + + if (drop_unencrypted >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, + drop_unencrypted); + if (userspace_mlme >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, + userspace_mlme); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +#else /* NL80211_CMD_SET_VIF */ + return -1; +#endif /* NL80211_CMD_SET_VIF */ +} + + +static int wpa_driver_nl80211_set_userspace_mlme( + struct wpa_driver_nl80211_data *drv, int enabled) +{ + return nl80211_set_vif(drv, -1, enabled); +} + + +static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, + int ifidx) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to remove interface."); +} + + +static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx, err; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + flags = nlmsg_alloc(); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + + err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + + nlmsg_free(flags); + + if (err) + goto nla_put_failure; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", + ret); + return ret; + } + + ifidx = if_nametoindex(ifname); + if (ifidx <= 0) + return -1; + + return ifidx; +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_radiotap_iterator iter; + int ret; + int injected = 0, failed = 0, rxflags = 0; + struct ieee80211_rx_status rx_status; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " + "frame"); + return; + } + + os_memset(&rx_status, 0, sizeof(rx_status)); + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid " + "radiotap frame (%d)", ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(u16 *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO convert from freq/flags to channel number + * rx_status.channel = XXX; + */ + break; + case IEEE80211_RADIOTAP_RATE: + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + rx_status.ssi = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) { + wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, + len - iter.max_length, &rx_status); + } else if (failed) { + /* TX failure callback */ + } else { + /* TX success (ACK) callback */ + } +} + + +static int wpa_driver_nl80211_create_monitor_interface( + struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval, flags; + socklen_t optlen; + + os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); + + if (drv->monitor_ifidx < 0) + return -1; + + if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || + wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != + 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " + "UP", buf); + goto error; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + goto error; + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, + sizeof(ll)) < 0) { + perror("monitor socket bind"); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + perror("Failed to set socket priority"); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " + "read socket"); + goto error; + } + + return 0; + + error: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +{ + int s, ret; + struct sockaddr_nl local; + struct wpa_driver_nl80211_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (drv->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + if (drv->nl_handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err2; + } + + if (genl_connect(drv->nl_handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink"); + goto err3; + } + + drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); + if (drv->nl_cache == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto err3; + } + + drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); + if (drv->nl80211 == NULL) { + wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " + "found"); + goto err4; + } + + ret = nl_get_multicast_id(drv, "nl80211", "scan"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_handle, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for scan events: %d (%s)", + ret, strerror(-ret)); + goto err4; + } + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), + wpa_driver_nl80211_event_receive, drv, ctx); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + goto err5; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + goto err6; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + goto err6; + } + + eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, + ctx); + drv->wext_event_sock = s; + + wpa_driver_nl80211_finish_drv_init(drv); + + return drv; + +err6: + close(drv->ioctl_sock); +err5: + genl_family_put(drv->nl80211); +err4: + nl_cache_free(drv->nl_cache); +err3: + nl_handle_destroy(drv->nl_handle); +err2: + nl_cb_put(drv->nl_cb); +err1: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) +{ + int flags; + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_nl80211_flush_pmkid(drv); + + if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + wpa_driver_nl80211_get_range(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface + * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_nl80211_init(). + */ +static void wpa_driver_nl80211_deinit(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + int flags; + +#ifdef CONFIG_CLIENT_MLME + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + } + if (drv->monitor_ifidx > 0) + nl80211_remove_iface(drv, drv->monitor_ifidx); + if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + wpa_driver_nl80211_set_userspace_mlme(drv, 0); +#endif /* CONFIG_CLIENT_MLME */ + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + (void) wpa_driver_nl80211_set_bssid(drv, + (u8 *) "\x00\x00\x00\x00\x00\x00"); + + wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->wext_event_sock); + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->wext_event_sock); + close(drv->ioctl_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + os_free(drv); +} + + +/** + * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @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 + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0, timeout; + struct nl_msg *msg, *ssids; + + msg = nlmsg_alloc(); + ssids = nlmsg_alloc(); + if (!msg || !ssids) { + nlmsg_free(msg); + nlmsg_free(ssids); + return -1; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_TRIGGER_SCAN, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (ssid && ssid_len) { + /* Request an active scan for a specific SSID */ + NLA_PUT(ssids, 1, ssid_len, ssid); + } else { + /* Request an active scan for wildcard SSID */ + NLA_PUT(ssids, 1, 0, ""); + } + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 10; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +nla_put_failure: + nlmsg_free(ssids); + nlmsg_free(msg); + return ret; +} + + +static int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *res = arg; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + const u8 *ie; + size_t ie_len; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + } else { + ie = NULL; + ie_len = 0; + } + + r = os_zalloc(sizeof(*r) + ie_len); + if (r == NULL) + return NL_SKIP; + if (bss[NL80211_BSS_BSSID]) + os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), + ETH_ALEN); + if (bss[NL80211_BSS_FREQUENCY]) + r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_BEACON_INTERVAL]) + r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); + if (bss[NL80211_BSS_CAPABILITY]) + r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + if (bss[NL80211_BSS_SIGNAL_UNSPEC]) + r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + if (bss[NL80211_BSS_SIGNAL_MBM]) + r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + if (bss[NL80211_BSS_TSF]) + r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + r->ie_len = ie_len; + if (ie) + os_memcpy(r + 1, ie, ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return NL_SKIP; + } + tmp[res->num++] = r; + res->res = tmp; + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +static struct wpa_scan_results * +wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct wpa_scan_results *res; + int ret; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return 0; + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, + NL80211_CMD_GET_SCAN, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", + (unsigned long) res->num); + return res; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + wpa_scan_results_free(res); + return NULL; +} + + +static int wpa_driver_nl80211_get_range(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", + drv->capa.key_mgmt, drv->capa.enc); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_nl80211_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) +{ + struct wpa_driver_nl80211_data *drv = priv; + int err; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " + "seq_len=%lu key_len=%lu", + __func__, alg, addr, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + if (alg == WPA_ALG_NONE) { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_DEL_KEY, 0); + } else { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_NEW_KEY, 0); + NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC01); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC05); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + break; + default: + nlmsg_free(msg); + return -1; + } + } + + if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + } + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); + return -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_KEY, 0); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set default key " + "failed; err=%d", err); + return -1; + } + } + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); +} + + +static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, + reason_code); +} + + +static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +static int wpa_driver_nl80211_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_nl80211_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_nl80211_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->group_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; + if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_nl80211_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_mode(void *priv, int mode) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = -1, flags; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + else + goto try_again; + +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode"); + return -1; + +try_again: + /* mac80211 doesn't allow mode changes while the device is up, so + * take the device down, try to set the mode again, and bring the + * device back up. + */ + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : + NL80211_IFTYPE_STATION); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_ERROR, "Failed to set interface %s " + "mode", drv->ifname); + } + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_nl80211_get_ifflags(drv, &flags); + (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); + } + + return ret; +} + + +static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_nl80211_flush_pmkid(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +static int wpa_driver_nl80211_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_nl80211_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static int wpa_driver_nl80211_set_operstate(void *priv, int state) +{ + struct wpa_driver_nl80211_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_nl80211_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +#ifdef CONFIG_CLIENT_MLME +static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) +{ + if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " + "MLME"); + return -1; + } + if (wpa_driver_nl80211_create_monitor_interface(drv)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " + "interface"); + return -1; + } + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_nl80211_set_param(void *priv, const char *param) +{ +#ifdef CONFIG_CLIENT_MLME + struct wpa_driver_nl80211_data *drv = priv; + + if (param == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); + drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + if (wpa_driver_nl80211_open_mlme(drv)) + return -1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME + +struct phy_info_arg { + u16 *num_modes; + struct wpa_hw_modes *modes; +}; + + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] + = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + }; + + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = + { .type = NLA_FLAG }, + }; + + struct nlattr *nl_band; + struct nlattr *nl_freq; + struct nlattr *nl_rate; + int rem_band, rem_freq, rem_rate; + struct wpa_hw_modes *mode; + int idx, mode_is_set; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], + rem_band) { + mode = os_realloc(phy_info->modes, + (*phy_info->num_modes + 1) * sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode_is_set = 0; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + *(phy_info->num_modes) += 1; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), + freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + mode->num_channels++; + } + + mode->channels = os_zalloc(mode->num_channels * + sizeof(struct wpa_channel_data)); + if (!mode->channels) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), + freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + mode->channels[idx].freq = nla_get_u32( + tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + mode->channels[idx].flag |= WPA_CHAN_W_SCAN | + WPA_CHAN_W_ACTIVE_SCAN | + WPA_CHAN_W_IBSS; + + if (!mode_is_set) { + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + mode->mode = WPA_MODE_IEEE80211B; + else + mode->mode = WPA_MODE_IEEE80211A; + mode_is_set = 1; + } + + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) { + if (mode->channels[idx].freq == 2848) + mode->channels[idx].chan = 14; + else + mode->channels[idx].chan = + (mode->channels[idx].freq - + 2407) / 5; + } else + mode->channels[idx].chan = + mode->channels[idx].freq / 5 - 1000; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].flag &= ~WPA_CHAN_W_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) + mode->channels[idx].flag &= + ~WPA_CHAN_W_ACTIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + mode->channels[idx].flag &= ~WPA_CHAN_W_IBSS; + idx++; + } + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], + rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = os_zalloc(mode->num_rates * + sizeof(struct wpa_rate_data)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], + rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx].rate = nla_get_u32( + tb_rate[NL80211_BITRATE_ATTR_RATE]); + + /* crude heuristic */ + if (mode->mode == WPA_MODE_IEEE80211B && + mode->rates[idx].rate > 200) + mode->mode = WPA_MODE_IEEE80211G; + + if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) + mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; + + idx++; + } + } + + return NL_SKIP; +} + + +static struct wpa_hw_modes * +wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + }; + + *num_modes = 0; + *flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return result.modes; +nla_put_failure: + return NULL; +} + + +static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + return wpa_driver_nl80211_set_freq(priv, freq); +} + + +static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + __u8 rtap_hdr[] = { + 0x00, 0x00, /* radiotap version */ + 0x0e, 0x00, /* radiotap length */ + 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ + 0x0c, /* F_WEP | F_FRAG (encrypt/fragment if required) */ + 0x00, /* padding */ + 0x00, 0x00, /* RX and TX flags to indicate that */ + 0x00, 0x00, /* this is the injected frame directly */ + }; + struct iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void *) data, + .iov_len = data_len, + } + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = 2, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + + if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { + perror("send[MLME]"); + return -1; + } + + return 0; +} + + +static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + /* TODO: Get proper Association ID and listen interval */ + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, + supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + /* ignore EEXIST, this happens if a STA associates while associated */ + if (ret == -EEXIST || ret >= 0) + ret = 0; + +nla_put_failure: + return ret; +} + + +static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = 0; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .desc = "Linux nl80211/cfg80211", + .get_bssid = wpa_driver_nl80211_get_bssid, + .get_ssid = wpa_driver_nl80211_get_ssid, + .set_wpa = wpa_driver_nl80211_set_wpa, + .set_key = wpa_driver_nl80211_set_key, + .set_countermeasures = wpa_driver_nl80211_set_countermeasures, + .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, + .scan = wpa_driver_nl80211_scan, + .get_scan_results2 = wpa_driver_nl80211_get_scan_results, + .deauthenticate = wpa_driver_nl80211_deauthenticate, + .disassociate = wpa_driver_nl80211_disassociate, + .set_mode = wpa_driver_nl80211_set_mode, + .associate = wpa_driver_nl80211_associate, + .set_auth_alg = wpa_driver_nl80211_set_auth_alg, + .init = wpa_driver_nl80211_init, + .deinit = wpa_driver_nl80211_deinit, + .set_param = wpa_driver_nl80211_set_param, + .add_pmkid = wpa_driver_nl80211_add_pmkid, + .remove_pmkid = wpa_driver_nl80211_remove_pmkid, + .flush_pmkid = wpa_driver_nl80211_flush_pmkid, + .get_capa = wpa_driver_nl80211_get_capa, + .set_operstate = wpa_driver_nl80211_set_operstate, + .set_country = wpa_driver_nl80211_set_country, + .set_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie, +#ifdef CONFIG_CLIENT_MLME + .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .set_channel = wpa_driver_nl80211_set_channel, + .set_ssid = wpa_driver_nl80211_set_ssid, + .set_bssid = wpa_driver_nl80211_set_bssid, + .send_mlme = wpa_driver_nl80211_send_mlme, + .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, + .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, +#endif /* CONFIG_CLIENT_MLME */ +}; diff --git a/src/drivers/driver_osx.m b/src/drivers/driver_osx.m new file mode 100644 index 0000000000000..93d7df01adfe0 --- /dev/null +++ b/src/drivers/driver_osx.m @@ -0,0 +1,432 @@ +/* + * WPA Supplicant - Mac OS X Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" + +#include "Apple80211.h" + +struct wpa_driver_osx_data { + void *ctx; + WirelessRef wireless_ctx; + CFArrayRef scan_results; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +extern int wpa_debug_level; + +static void dump_dict_cb(const void *key, const void *value, void *context) +{ + if (MSG_DEBUG < wpa_debug_level) + return; + + wpa_printf(MSG_DEBUG, "Key:"); + CFShow(key); + wpa_printf(MSG_DEBUG, "Value:"); + CFShow(value); +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries", + title, (unsigned int) CFDictionaryGetCount(dict)); + CFDictionaryApplyFunction(dict, dump_dict_cb, NULL); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + int len; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + for (len = 0; len < 32; len++) + if (info.ssid[len] == 0) + break; + + os_memcpy(ssid, info.ssid, len); + return len; +} + + +static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + os_memcpy(bssid, info.bssID, ETH_ALEN); + return 0; +} + + +static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_osx_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + if (ssid) { + CFStringRef data; + data = CFStringCreateWithBytes(kCFAllocatorDefault, + ssid, ssid_len, + kCFStringEncodingISOLatin1, + FALSE); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes " + "failed"); + return -1; + } + + err = WirelessDirectedScan(drv->wireless_ctx, + &drv->scan_results, 0, data); + CFRelease(data); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan " + "failed: 0x%08x", (unsigned int) err); + return -1; + } + } else { + err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: " + "0x%08x", (unsigned int) err); + return -1; + } + } + + eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_osx_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_osx_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + WirelessNetworkInfo *info; + info = (WirelessNetworkInfo *) + CFDataGetBytePtr(CFArrayGetValueAtIndex( + drv->scan_results, i)); + + os_memcpy(res->bssid, info->bssid, ETH_ALEN); + if (info->ssid_len > 32) { + wpa_printf(MSG_DEBUG, "OSX: Invalid SSID length %d in " + "scan results", (int) info->ssid_len); + continue; + } + os_memcpy(res->ssid, info->ssid, info->ssid_len); + res->ssid_len = info->ssid_len; + res->caps = info->capability; + res->freq = 2407 + info->channel * 5; + res->level = info->signal; + res->noise = info->noise; + } + + return num; +} + + +static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_osx_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + CFDictionaryRef ai; + + if (wpa_driver_osx_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout, + drv, drv->ctx); + return; + } + + ai = WirelessGetAssociationInfo(drv->wireless_ctx); + if (ai) { + wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo"); + CFRelease(ai); + } else { + wpa_printf(MSG_DEBUG, "OSX: Failed to get association info"); + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_osx_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + CFDataRef ssid; + CFStringRef key; + int assoc_type; + + ssid = CFDataCreate(kCFAllocatorDefault, params->ssid, + params->ssid_len); + if (ssid == NULL) + return -1; + + /* TODO: support for WEP */ + if (params->key_mgmt_suite == KEY_MGMT_PSK) { + if (params->passphrase == NULL) + return -1; + key = CFStringCreateWithCString(kCFAllocatorDefault, + params->passphrase, + kCFStringEncodingISOLatin1); + if (key == NULL) { + CFRelease(ssid); + return -1; + } + } else + key = NULL; + + if (params->key_mgmt_suite == KEY_MGMT_NONE) + assoc_type = 0; + else + assoc_type = 4; + + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)", + assoc_type, key); + err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key); + CFRelease(ssid); + if (key) + CFRelease(key); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x", + (unsigned int) err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_osx_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) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (alg == WPA_ALG_WEP) { + err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len, + key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + + return 0; + } + + if (alg == WPA_ALG_PMK) { + err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + return 0; + } + + wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg); + return -1; +} + + +static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_osx_init(void *ctx, const char *ifname) +{ + struct wpa_driver_osx_data *drv; + WirelessError err; + u8 enabled, power; + + if (!WirelessIsAvailable()) { + wpa_printf(MSG_ERROR, "OSX: No wireless interface available"); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = WirelessAttach(&drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d", + (int) err); + os_free(drv); + return NULL; + } + + err = WirelessGetEnabled(drv->wireless_ctx, &enabled); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x", + (unsigned int) err); + err = WirelessGetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x", + (unsigned int) err); + + wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power); + + if (!enabled) { + err = WirelessSetEnabled(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:" + " 0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + if (!power) { + err = WirelessSetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: " + "0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + return drv; +} + + +static void wpa_driver_osx_deinit(void *priv) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + + err = WirelessSetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: " + "0x%08x", (unsigned int) err); + } + + err = WirelessDetach(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x", + (unsigned int) err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_osx_ops = { + .name = "osx", + .desc = "Mac OS X Apple80211 driver", + .get_ssid = wpa_driver_osx_get_ssid, + .get_bssid = wpa_driver_osx_get_bssid, + .init = wpa_driver_osx_init, + .deinit = wpa_driver_osx_deinit, + .scan = wpa_driver_osx_scan, + .get_scan_results = wpa_driver_osx_get_scan_results, + .associate = wpa_driver_osx_associate, + .set_key = wpa_driver_osx_set_key, + .get_capa = wpa_driver_osx_get_capa, +}; diff --git a/src/drivers/driver_prism54.c b/src/drivers/driver_prism54.c new file mode 100644 index 0000000000000..e64e762eddb54 --- /dev/null +++ b/src/drivers/driver_prism54.c @@ -0,0 +1,381 @@ +/* + * WPA Supplicant - driver interaction with Linux Prism54.org driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "driver_hostap.h" + +struct wpa_driver_prism54_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 +#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25 +#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26 + +static void show_set_key_error(struct prism2_hostapd_param *); + +static int hostapd_ioctl_prism54(struct wpa_driver_prism54_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM54_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM54_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_prism54_set_wpa_ie(struct wpa_driver_prism54_data *drv, + const u8 *wpa_ie, + size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +/* This is called at wpa_supplicant daemon init time */ +static int wpa_driver_prism54_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_SET_WPA; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_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) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + return -1; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + return -1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + return -1; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl_prism54(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_prism54_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_prism54_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_DROP_UNENCRYPTED; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_deauthenticate - not yet implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_disassociate - not yet implemented\n"); + return 0; +} + + +static int +wpa_driver_prism54_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_prism54_data *drv = priv; + int ret = 0; + + if (wpa_driver_prism54_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + return ret; +} + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_prism54_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_prism54_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_prism54_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * +wpa_driver_prism54_get_scan_results(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_prism54_set_operstate(void *priv, int state) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_prism54_init(void *ctx, const char *ifname) +{ + struct wpa_driver_prism54_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_prism54_deinit(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_prism54_ops = { + .name = "prism54", + .desc = "Prism54.org driver (Intersil Prism GT/Duette/Indigo)", + .get_bssid = wpa_driver_prism54_get_bssid, + .get_ssid = wpa_driver_prism54_get_ssid, + .set_wpa = wpa_driver_prism54_set_wpa, + .set_key = wpa_driver_prism54_set_key, + .set_countermeasures = wpa_driver_prism54_set_countermeasures, + .set_drop_unencrypted = wpa_driver_prism54_set_drop_unencrypted, + .scan = wpa_driver_prism54_scan, + .get_scan_results2 = wpa_driver_prism54_get_scan_results, + .deauthenticate = wpa_driver_prism54_deauthenticate, + .disassociate = wpa_driver_prism54_disassociate, + .associate = wpa_driver_prism54_associate, + .init = wpa_driver_prism54_init, + .deinit = wpa_driver_prism54_deinit, + .set_operstate = wpa_driver_prism54_set_operstate, +}; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c new file mode 100644 index 0000000000000..fdf299dc84e44 --- /dev/null +++ b/src/drivers/driver_privsep.c @@ -0,0 +1,820 @@ +/* + * WPA Supplicant - privilege separated driver interface + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "privsep_commands.h" + + +struct wpa_driver_privsep_data { + void *ctx; + u8 own_addr[ETH_ALEN]; + int priv_socket; + char *own_socket_path; + int cmd_socket; + char *own_cmd_path; + struct sockaddr_un priv_addr; + char ifname[16]; +}; + + +static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) +{ + int res; + + res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, + (struct sockaddr *) &drv->priv_addr, + sizeof(drv->priv_addr)); + if (res < 0) + perror("sendto"); + return res < 0 ? -1 : 0; +} + + +static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, + const void *data, size_t data_len, + void *reply, size_t *reply_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &drv->priv_addr; + msg.msg_namelen = sizeof(drv->priv_addr); + + if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { + perror("sendmsg(cmd_socket)"); + return -1; + } + + if (reply) { + fd_set rfds; + struct timeval tv; + int res; + + FD_ZERO(&rfds); + FD_SET(drv->cmd_socket, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + return -1; + } + + if (FD_ISSET(drv->cmd_socket, &rfds)) { + res = recv(drv->cmd_socket, reply, *reply_len, 0); + if (res < 0) { + perror("recv"); + return -1; + } + *reply_len = res; + } else { + wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " + "for reply (cmd=%d)", cmd); + return -1; + } + } + + return 0; +} + + +static int wpa_driver_privsep_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_WPA, &enabled, + sizeof(enabled), NULL, NULL); +} + + +static int wpa_driver_privsep_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + NULL, NULL); +} + + +static struct wpa_scan_results * +wpa_driver_privsep_get_scan_results2(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, num; + u8 *buf, *pos, *end; + size_t reply_len = 60000; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + buf = os_malloc(reply_len); + if (buf == NULL) + return NULL; + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, + NULL, 0, buf, &reply_len); + if (res < 0) { + os_free(buf); + return NULL; + } + + wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", + (unsigned long) reply_len); + if (reply_len < sizeof(int)) { + wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", + (unsigned long) reply_len); + os_free(buf); + return NULL; + } + + pos = buf; + end = buf + reply_len; + os_memcpy(&num, pos, sizeof(int)); + if (num < 0 || num > 1000) { + os_free(buf); + return NULL; + } + pos += sizeof(int); + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(buf); + return NULL; + } + + results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(buf); + return NULL; + } + + while (results->num < (size_t) num && pos + sizeof(int) < end) { + int len; + os_memcpy(&len, pos, sizeof(int)); + pos += sizeof(int); + if (len < 0 || len > 10000 || pos + len > end) + break; + + r = os_malloc(len); + if (r == NULL) + break; + os_memcpy(r, pos, len); + pos += len; + if (sizeof(*r) + r->ie_len > (size_t) len) { + os_free(r); + break; + } + + results->res[results->num++] = r; + } + + os_free(buf); + return results; +} + + +static int wpa_driver_privsep_set_key(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) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_set_key cmd; + + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + + os_memset(&cmd, 0, sizeof(cmd)); + cmd.alg = alg; + if (addr) + os_memcpy(cmd.addr, addr, ETH_ALEN); + else + os_memset(cmd.addr, 0xff, ETH_ALEN); + cmd.key_idx = key_idx; + cmd.set_tx = set_tx; + if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { + os_memcpy(cmd.seq, seq, seq_len); + cmd.seq_len = seq_len; + } + if (key && key_len > 0 && key_len < sizeof(cmd.key)) { + os_memcpy(cmd.key, key, key_len); + cmd.key_len = key_len; + } + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), + NULL, NULL); +} + + +static int wpa_driver_privsep_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_associate *data; + int res; + size_t buflen; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + + buflen = sizeof(*data) + params->wpa_ie_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + if (params->bssid) + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->freq = params->freq; + data->pairwise_suite = params->pairwise_suite; + data->group_suite = params->group_suite; + data->key_mgmt_suite = params->key_mgmt_suite; + data->auth_alg = params->auth_alg; + data->mode = params->mode; + data->wpa_ie_len = params->wpa_ie_len; + if (params->wpa_ie) + os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); + /* TODO: add support for other assoc parameters */ + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + +static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = ETH_ALEN; + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); + if (res < 0 || len != ETH_ALEN) + return -1; + return 0; +} + + +static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, ssid_len; + u8 reply[sizeof(int) + 32]; + size_t len = sizeof(reply); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); + if (res < 0 || len < sizeof(int)) + return -1; + os_memcpy(&ssid_len, reply, sizeof(int)); + if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); + return -1; + } + os_memcpy(ssid, &reply[sizeof(int)], ssid_len); + return ssid_len; +} + + +static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static void wpa_driver_privsep_event_assoc(void *ctx, wpa_event_type event, + u8 *buf, size_t len) +{ + union wpa_event_data data; + int inc_data = 0; + u8 *pos, *end; + int ie_len; + + os_memset(&data, 0, sizeof(data)); + + pos = buf; + end = buf + len; + + if (end - pos < (int) sizeof(int)) + return; + os_memcpy(&ie_len, pos, sizeof(int)); + pos += sizeof(int); + if (ie_len < 0 || ie_len > end - pos) + return; + if (ie_len) { + data.assoc_info.req_ies = pos; + data.assoc_info.req_ies_len = ie_len; + pos += ie_len; + inc_data = 1; + } + + wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); +} + + +static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + int ievent; + + if (len < sizeof(int) || + len - sizeof(int) > sizeof(data.interface_status.ifname)) + return; + + os_memcpy(&ievent, buf, sizeof(int)); + + os_memset(&data, 0, sizeof(data)); + data.interface_status.ievent = ievent; + os_memcpy(data.interface_status.ifname, buf + sizeof(int), + len - sizeof(int)); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); +} + + +static void wpa_driver_privsep_event_michael_mic_failure( + void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(int)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(struct pmkid_candidate)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.pmkid_candidate, buf, len); + wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.stkstart.peer, buf, ETH_ALEN); + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +} + + +static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len < sizeof(int) + ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); + os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); + data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; + data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; + wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); +} + + +static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) +{ + if (len < ETH_ALEN) + return; + + wpa_supplicant_rx_eapol(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); +} + + +static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status *rx_status; + + if (len < sizeof(*rx_status)) + return; + rx_status = (struct ieee80211_rx_status *) buf; + buf += sizeof(*rx_status); + len -= sizeof(*rx_status); + + wpa_supplicant_sta_rx(ctx, buf, len, rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_privsep_data *drv = eloop_ctx; + u8 *buf, *event_buf; + size_t event_len; + int res, event; + enum privsep_event e; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(priv_socket)"); + os_free(buf); + return; + } + + wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); + return; + } + + os_memcpy(&event, buf, sizeof(int)); + event_buf = &buf[sizeof(int)]; + event_len = res - sizeof(int); + wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", + event, (unsigned long) event_len); + + e = event; + switch (e) { + case PRIVSEP_EVENT_SCAN_RESULTS: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case PRIVSEP_EVENT_ASSOC: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, + event_buf, event_len); + break; + case PRIVSEP_EVENT_DISASSOC: + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + break; + case PRIVSEP_EVENT_ASSOCINFO: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, + event_buf, event_len); + break; + case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: + wpa_driver_privsep_event_michael_mic_failure( + drv->ctx, event_buf, event_len); + break; + case PRIVSEP_EVENT_INTERFACE_STATUS: + wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_PMKID_CANDIDATE: + wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STKSTART: + wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_FT_RESPONSE: + wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_RX_EAPOL: + wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STA_RX: + wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf, + event_len); + break; + } + + os_free(buf); +} + + +static void * wpa_driver_privsep_init(void *ctx, const char *ifname) +{ + struct wpa_driver_privsep_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + drv->priv_socket = -1; + drv->cmd_socket = -1; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; +} + + +static void wpa_driver_privsep_deinit(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + + if (drv->priv_socket >= 0) { + wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); + eloop_unregister_read_sock(drv->priv_socket); + close(drv->priv_socket); + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + } + + if (drv->cmd_socket >= 0) { + eloop_unregister_read_sock(drv->cmd_socket); + close(drv->cmd_socket); + } + + if (drv->own_cmd_path) { + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + } + + os_free(drv); +} + + +static int wpa_driver_privsep_set_param(void *priv, const char *param) +{ + struct wpa_driver_privsep_data *drv = priv; + const char *pos; + char *own_dir, *priv_dir; + static unsigned int counter = 0; + size_t len; + struct sockaddr_un addr; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "own_dir="); + if (pos) { + char *end; + own_dir = os_strdup(pos + 8); + if (own_dir == NULL) + return -1; + end = os_strchr(own_dir, ' '); + if (end) + *end = '\0'; + } else { + own_dir = os_strdup("/tmp"); + if (own_dir == NULL) + return -1; + } + + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "priv_dir="); + if (pos) { + char *end; + priv_dir = os_strdup(pos + 9); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + end = os_strchr(priv_dir, ' '); + if (end) + *end = '\0'; + } else { + priv_dir = os_strdup("/var/run/wpa_priv"); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + } + + len = os_strlen(own_dir) + 50; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) { + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + len = os_strlen(own_dir) + 50; + drv->own_cmd_path = os_malloc(len); + if (drv->own_cmd_path == NULL) { + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + os_free(own_dir); + + drv->priv_addr.sun_family = AF_UNIX; + os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), + "%s/%s", priv_dir, drv->ifname); + os_free(priv_dir); + + drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->priv_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + close(drv->priv_socket); + drv->priv_socket = -1; + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, + drv, NULL); + + drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->cmd_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); + if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("bind(PF_UNIX)"); + close(drv->cmd_socket); + drv->cmd_socket = -1; + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { + wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); + return -1; + } + + return 0; +} + + +static int wpa_driver_privsep_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = sizeof(*capa); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); + if (res < 0 || len != sizeof(*capa)) + return -1; + return 0; +} + + +static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_privsep_set_mode(void *priv, int mode) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode), + NULL, NULL); +} + + +static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, + os_strlen(alpha2), NULL, NULL); +} + + +struct wpa_driver_ops wpa_driver_privsep_ops = { + "privsep", + "wpa_supplicant privilege separated driver", + wpa_driver_privsep_get_bssid, + wpa_driver_privsep_get_ssid, + wpa_driver_privsep_set_wpa, + wpa_driver_privsep_set_key, + wpa_driver_privsep_init, + wpa_driver_privsep_deinit, + wpa_driver_privsep_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_privsep_scan, + NULL /* get_scan_results */, + wpa_driver_privsep_deauthenticate, + wpa_driver_privsep_disassociate, + wpa_driver_privsep_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_privsep_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_privsep_get_mac_addr, + NULL /* send_eapol */, + NULL /* set_operstate */, + NULL /* mlme_setprotection */, + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_privsep_get_scan_results2, + NULL /* set_probe_req_ie */, + wpa_driver_privsep_set_mode, + wpa_driver_privsep_set_country, + NULL /* global_init */, + NULL /* global_deinit */, + NULL /* init2 */, + NULL /* get_interfaces */ +}; + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ + &wpa_driver_privsep_ops, + NULL +}; diff --git a/src/drivers/driver_ps3.c b/src/drivers/driver_ps3.c new file mode 100644 index 0000000000000..fde3425e281f3 --- /dev/null +++ b/src/drivers/driver_ps3.c @@ -0,0 +1,186 @@ +/* + * WPA Supplicant - PS3 Linux wireless extension driver interface + * Copyright 2007, 2008 Sony Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include "wireless_copy.h" +#include "common.h" +#include "wpa_common.h" +#include "driver.h" +#include "eloop.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" + +static int wpa_driver_ps3_set_wpa_key(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + char *buf, *str; + + if (!params->psk && !params->passphrase) { + wpa_printf(MSG_INFO, "%s:no PSK error", __func__); + return -EINVAL; + } + + os_memset(&iwr, 0, sizeof(iwr)); + if (params->psk) { + /* includes null */ + iwr.u.data.length = PMK_LEN * 2 + 1; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + str = buf; + for (i = 0; i < PMK_LEN; i++) { + str += snprintf(str, iwr.u.data.length - (str - buf), + "%02x", params->psk[i]); + } + } else if (params->passphrase) { + /* including quotations and null */ + iwr.u.data.length = strlen(params->passphrase) + 3; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + buf[0] = '"'; + os_memcpy(buf + 1, params->passphrase, iwr.u.data.length - 3); + buf[iwr.u.data.length - 2] = '"'; + buf[iwr.u.data.length - 1] = '\0'; + } else + return -EINVAL; + iwr.u.data.pointer = (caddr_t) buf; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + ret = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); + os_free(buf); + + return ret; +} + +static int wpa_driver_ps3_set_wep_keys(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + + for (i = 0; i < 4; i++) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = i + 1; + if (params->wep_key_len[i]) { + iwr.u.encoding.pointer = (caddr_t) params->wep_key[i]; + iwr.u.encoding.length = params->wep_key_len[i]; + } else + iwr.u.encoding.flags = IW_ENCODE_NOKEY | + IW_ENCODE_DISABLED; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + } + return ret; +} + +static int wpa_driver_ps3_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret, value; + + wpa_printf(MSG_DEBUG, "%s: <-", __func__); + + /* clear BSSID */ + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + if (wpa_driver_wext_set_mode(drv, params->mode) < 0) + ret = -1; + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + + /* set selected BSSID */ + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + switch (params->group_suite) { + case CIPHER_NONE: + ret = 0; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + ret = wpa_driver_ps3_set_wep_keys(drv, params); + break; + case CIPHER_TKIP: + case CIPHER_CCMP: + ret = wpa_driver_ps3_set_wpa_key(drv, params); + break; + } + + /* start to associate */ + ret = wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len); + + wpa_printf(MSG_DEBUG, "%s: ->", __func__); + + return ret; +} + +static int wpa_driver_ps3_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + int ret; + wpa_printf(MSG_DEBUG, "%s:<-", __func__); + + ret = wpa_driver_wext_get_capa(priv, capa); + if (ret) { + wpa_printf(MSG_INFO, "%s: base wext returns error %d", + __func__, ret); + return ret; + } + /* PS3 hypervisor does association and 4way handshake by itself */ + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + wpa_printf(MSG_DEBUG, "%s:->", __func__); + return 0; +} + +const struct wpa_driver_ops wpa_driver_ps3_ops = { + .name = "ps3", + .desc = "PLAYSTATION3 Linux wireless extension driver", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .associate = wpa_driver_ps3_associate, /* PS3 */ + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .get_capa = wpa_driver_ps3_get_capa, /* PS3 */ +}; diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c new file mode 100644 index 0000000000000..e9313cb33ef09 --- /dev/null +++ b/src/drivers/driver_ralink.c @@ -0,0 +1,1505 @@ +/* + * WPA Supplicant - driver interaction with Ralink Wireless Client + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "priv_netlink.h" +#include "driver_ralink.h" + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +#define MAX_SSID_LEN 32 + +struct wpa_driver_ralink_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + int no_of_pmkid; + struct ndis_pmkid_entry *pmkid; + int we_version_compiled; + int ap_scan; + int scanning_done; + u8 g_driver_down; +}; + +static int ralink_set_oid(struct wpa_driver_ralink_data *drv, + unsigned short oid, char *data, int len) +{ + char *buf; + struct iwreq iwr; + + buf = os_zalloc(len); + if (buf == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.flags = oid; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + + if (data) + os_memcpy(buf, data, len); + + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + os_free(buf); + return 0; +} + +static int +ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UCHAR enabled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (UCHAR*) &enabled; + iwr.u.data.flags = RT_OID_NEW_DRIVER; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return 0; + } + + return (enabled == 1) ? 1 : 0; +} + +static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + +static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ralink_data *drv = priv; +#if 0 + struct wpa_supplicant *wpa_s = drv->ctx; + struct wpa_ssid *entry; +#endif + int ssid_len; + u8 bssid[ETH_ALEN]; + u8 ssid_str[MAX_SSID_LEN]; + struct iwreq iwr; +#if 0 + int result = 0; +#endif + int ret = 0; +#if 0 + BOOLEAN ieee8021x_mode = FALSE; + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + if (ret <= 0) + return ret; + + ssid_len = ret; + os_memset(ssid_str, 0, MAX_SSID_LEN); + os_memcpy(ssid_str, ssid, ssid_len); + + if (drv->ap_scan == 0) { + /* Read BSSID form driver */ + if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { + wpa_printf(MSG_WARNING, "Could not read BSSID from " + "driver."); + return ret; + } + +#if 0 + entry = wpa_s->conf->ssid; + while (entry) { + if (!entry->disabled && ssid_len == entry->ssid_len && + os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { + /* match the config of driver */ + result = 1; + break; + } + entry = entry->next; + } + + if (result) { + wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " + "ieee_required_keys parameters to driver"); + + /* set 802.1x mode and ieee_required_keys parameter */ + if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) + ieee8021x_required_key = TRUE; + ieee8021x_mode = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", + entry->eapol_flags); + } + } +#endif + } + + return ret; +} + +static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID *buf; + int ret = 0; + struct iwreq iwr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + buf = os_zalloc(sizeof(NDIS_802_11_SSID)); + if (buf == NULL) + return -1; + os_memset(buf, 0, sizeof(buf)); + buf->SsidLength = ssid_len; + os_memcpy(buf->Ssid, ssid, ssid_len); + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + iwr.u.data.flags = OID_802_11_SSID; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = sizeof(NDIS_802_11_SSID); + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); + ret = -1; + } + os_free(buf); + return ret; +} + +static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " + "Event (len=%lu)", (unsigned long) data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" + " NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " + "List Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " + "underflow"); + + return; + } + + + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", + (unsigned long) i, MAC2STR(p->BSSID), + (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + +static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", + (const u8 *) p, len); + ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + +static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + drv->pmkid = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_flush_pmkid(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (const u8 *) &p, 8); + return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); +} + +static void +wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, + void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + /* receive a MICFAILURE report */ + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { + /* receive assoc. req. IEs */ + char *spos; + int bytes; + + spos = custom + 17; + /*get IE's length */ + /* + * bytes = strlen(spos); ==> bug, bytes may less than original + * size by using this way to get size. snowpin 20070312 + * if (!bytes) + * return; + */ + bytes = drv->assoc_req_ies_len; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + os_memcpy(data.assoc_info.req_ies, spos, bytes); + + /* skip the '\0' byte */ + spos += bytes + 1; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + /* receive assoc. resp. IEs */ + spos += 9; + /* get IE's length */ + bytes = os_strlen(spos); + if (!bytes) + goto done; + + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + os_memcpy(data.assoc_info.resp_ies, spos, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + /* free allocated memory */ + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); + } +} + +static void +wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; +#if 0 + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + assoc_info_buf = info_pos = NULL; + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + + if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + + if (drv->ap_scan == 1) { + if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) + || (iwe->u.data.flags == + RT_REQIE_EVENT_FLAG) || + (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) + || (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG)) { + if (drv->scanning_done == 0) { + os_free(buf); + return; + } + } + } + + if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCIATED_EVENT !!!"); + /* determine whether the dynamic-WEP is used or + * not */ +#if 0 + if (wpa_s && wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == + WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", + // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); + ieee8021x_required_key = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", + (int) ieee8021x_required_key); + } + + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", + wpa_s->current_ssid->eapol_flags); + } +#endif + + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ReqIEs !!!"); + drv->assoc_req_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_req_ies == NULL) { + os_free(buf); + return; + } + + drv->assoc_req_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_req_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive RespIEs !!!"); + drv->assoc_resp_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_resp_ies == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(buf); + return; + } + + drv->assoc_resp_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_resp_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCINFO_EVENT !!!"); + + assoc_info_buf = + os_zalloc(drv->assoc_req_ies_len + + drv->assoc_resp_ies_len + 1); + + if (assoc_info_buf == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(buf); + return; + } + + if (drv->assoc_req_ies) { + os_memcpy(assoc_info_buf, + drv->assoc_req_ies, + drv->assoc_req_ies_len); + } + info_pos = assoc_info_buf + + drv->assoc_req_ies_len; + if (drv->assoc_resp_ies) { + os_memcpy(info_pos, + drv->assoc_resp_ies, + drv->assoc_resp_ies_len); + } + assoc_info_buf[drv->assoc_req_ies_len + + drv->assoc_resp_ies_len] = '\0'; + wpa_driver_ralink_event_wireless_custom( + drv, ctx, assoc_info_buf); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(assoc_info_buf); + } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) + { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive DISASSOCIATED_EVENT !!!"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive PMKIDCAND_EVENT !!!"); + wpa_driver_ralink_event_pmkid( + drv, (const u8 *) custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { + drv->g_driver_down = 1; + eloop_terminate(); + } else if (iwe->u.data.flags == RT_REPORT_AP_INFO) { + if (drv->ap_scan != 1) { + typedef struct PACKED { + UCHAR bssid[MAC_ADDR_LEN]; + UCHAR ssid[MAX_LEN_OF_SSID]; + INT ssid_len; + UCHAR wpa_ie[40]; + INT wpa_ie_len; + UCHAR rsn_ie[40]; + INT rsn_ie_len; + INT freq; + USHORT caps; + } *PAPINFO; + + wpa_printf(MSG_DEBUG, "Custom wireless" + " event: receive " + "RT_REPORT_AP_INFO !!!"); + //printf("iwe->u.data.length = %d\n", iwe->u.data.length); + //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); +#if 0 + wpa_s->num_scan_results = 1; + if (wpa_s->scan_results) + os_free(wpa_s->scan_results); + wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); + if (wpa_s->scan_results) { + PAPINFO pApInfo = (PAPINFO)buf; + os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); + os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); + wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; + if (pApInfo->wpa_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); + wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; + } else if (pApInfo->rsn_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); + wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; + } + wpa_s->scan_results[0].caps = pApInfo->caps; + wpa_s->scan_results[0].freq = pApInfo->freq; + } else { + wpa_printf("wpa_s->scan_" + "results fail to " + "os_malloc!!\n"); + } +#endif + } + } else { + wpa_driver_ralink_event_wireless_custom( + drv, ctx, buf); + } + os_free(buf); + break; + } + + pos += iwe->len; + } +} + +static void +wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, + void *ctx, struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); + while (RTA_OK(attr, attrlen)) { + wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_ralink_event_wireless( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + wpa_hexdump(MSG_DEBUG, "attr3: ", + (u8 *) attr, sizeof(struct rtattr)); + } +} + +static void wpa_driver_ralink_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); + + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, + plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + +} + +static int +ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UINT we_version_compiled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) &we_version_compiled; + iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return -1; + } + + drv->we_version_compiled = we_version_compiled; + + return 0; +} + +static int +ralink_set_iface_flags(void *priv, int dev_up) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->ioctl_sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + return 0; +} + +static void * wpa_driver_ralink_init(void *ctx, const char *ifname) +{ + int s; + struct wpa_driver_ralink_data *drv; + struct ifreq ifr; + struct sockaddr_nl local; + UCHAR enable_wpa_supplicant = 0; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->scanning_done = 1; + drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + drv->g_driver_down = 0; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); + drv->event_sock = s; + drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ + + ralink_set_iface_flags(drv, 1); /* mark up during setup */ + ralink_get_we_version_compiled(drv); + wpa_driver_ralink_flush_pmkid(drv); + + if (drv->ap_scan == 1) + enable_wpa_supplicant = 1; + else + enable_wpa_supplicant = 2; + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + wpa_printf(MSG_ERROR, "RALINK: Driver does not support " + "wpa_supplicant"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + if (drv->ap_scan == 1) + drv->scanning_done = 0; + + return drv; +} + +static void wpa_driver_ralink_deinit(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR enable_wpa_supplicant; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + enable_wpa_supplicant = 0; + + if (drv->g_driver_down == 0) { + /* trigger driver disable wpa_supplicant support */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (char *) &enable_wpa_supplicant, + sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + } + + wpa_driver_ralink_flush_pmkid(drv); + + sleep(1); + ralink_set_iface_flags(drv, 0); + } + + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ralink_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); + + drv->scanning_done = 1; + +} + +static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, + drv->ctx); + + drv->scanning_done = 0; + + return ret; +} + +static int +wpa_driver_ralink_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR *buf = NULL; + NDIS_802_11_BSSID_LIST_EX *wsr; + NDIS_WLAN_BSSID_EX *wbi; + struct iwreq iwr; + int rv = 0; + size_t ap_num; + u8 *pos, *end; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->we_version_compiled >= 17) { + buf = os_zalloc(8192); + iwr.u.data.length = 8192; + } else { + buf = os_zalloc(4096); + iwr.u.data.length = 4096; + } + if (buf == NULL) + return -1; + + wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; + + wsr->NumberOfItems = 0; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (void *) buf; + iwr.u.data.flags = OID_802_11_BSSID_LIST; + + if ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { + wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; + ++ap_num) { + os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, + wbi->Ssid.SsidLength); + results[ap_num].ssid_len = wbi->Ssid.SsidLength; + results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); + + /* get ie's */ + wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", + (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); + + pos = (u8 *) wbi + sizeof(*wbi) - 1; + end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; + + if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) + break; + + pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; + os_memcpy(&results[ap_num].caps, pos, 2); + pos += 2; + + while (pos + 1 < end && pos + 2 + pos[1] <= end) { + u8 ielen = 2 + pos[1]; + + if (ielen > SSID_MAX_WPA_IE_LEN) { + pos += ielen; + continue; + } + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { + os_memcpy(results[ap_num].wpa_ie, pos, ielen); + results[ap_num].wpa_ie_len = ielen; + } else if (pos[0] == WLAN_EID_RSN) { + os_memcpy(results[ap_num].rsn_ie, pos, ielen); + results[ap_num].rsn_ie_len = ielen; + } + pos += ielen; + } + + wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); + } + + os_free(buf); + return ap_num; +} + +static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, + NDIS_802_11_AUTHENTICATION_MODE mode) +{ + NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + +static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX _index; + int res, res2; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + + if (pairwise) + rkey.KeyIndex |= 1 << 30; + + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + + /* AlbertY@20060210 removed it */ + if (0 /* !pairwise */) { + res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &_index, sizeof(_index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return res; + return 0; +} + +static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + + wep->Length = len; + wep->KeyIndex = key_idx; + + if (set_tx) + wep->KeyIndex |= 0x80000000; + + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", + (const u8 *) wep, len); + res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + +static int wpa_driver_ralink_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) +{ + struct wpa_driver_ralink_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* Group Key */ + pairwise = 0; + wpa_driver_ralink_get_bssid(drv, bssid); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, + set_tx, key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + + if (set_tx) + nkey->KeyIndex |= 1 << 31; + + if (pairwise) + nkey->KeyIndex |= 1 << 30; + + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", + (const u8 *) nkey, len); + res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + +static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DISASSOCIATE"); + } + + return 0; +} + +static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_get_new_driver_flag(drv) == 0) { + return wpa_driver_ralink_disassociate(priv, addr, reason_code); + } else { + MLME_DEAUTH_REQ_STRUCT mlme; + os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); + mlme.Reason = reason_code; + os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); + return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, + (char *) &mlme, + sizeof(MLME_DEAUTH_REQ_STRUCT)); + } +} + +static int +wpa_driver_ralink_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ralink_data *drv = priv; + + NDIS_802_11_NETWORK_INFRASTRUCTURE mode; + NDIS_802_11_AUTHENTICATION_MODE auth_mode; + NDIS_802_11_WEP_STATUS encr; + BOOLEAN ieee8021xMode; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode == IEEE80211_MODE_IBSS) + mode = Ndis802_11IBSS; + else + mode = Ndis802_11Infrastructure; + + if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & AUTH_ALG_SHARED_KEY) { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; + } else { + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: + encr = Ndis802_11EncryptionDisabled; + break; + } + + ralink_set_auth_mode(drv, auth_mode); + + /* notify driver that IEEE8021x mode is enabled */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) + ieee8021xMode = TRUE; + else + ieee8021xMode = FALSE; + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, + (char *) &ieee8021xMode, sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_SET_IEEE8021X(%d)", + (int) ieee8021xMode); + } + + if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, + (char *) &encr, sizeof(encr)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_WEP_STATUS(%d)", + (int) encr); + } + + if ((ieee8021xMode == FALSE) && + (encr == Ndis802_11Encryption1Enabled)) { + /* static WEP */ + int enabled = 0; + if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, + (char *) &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DROP_UNENCRYPTED(%d)", + (int) encr); + } + } + + return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); +} + +static int +wpa_driver_ralink_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ralink_data *drv = priv; + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, + sizeof(int)); +} + +const struct wpa_driver_ops wpa_driver_ralink_ops = { + .name = "ralink", + .desc = "Ralink Wireless Client driver", + .get_bssid = wpa_driver_ralink_get_bssid, + .get_ssid = wpa_driver_ralink_get_ssid, + .set_key = wpa_driver_ralink_set_key, + .init = wpa_driver_ralink_init, + .deinit = wpa_driver_ralink_deinit, + .set_countermeasures = wpa_driver_ralink_set_countermeasures, + .scan = wpa_driver_ralink_scan, + .get_scan_results = wpa_driver_ralink_get_scan_results, + .deauthenticate = wpa_driver_ralink_deauthenticate, + .disassociate = wpa_driver_ralink_disassociate, + .associate = wpa_driver_ralink_associate, + .add_pmkid = wpa_driver_ralink_add_pmkid, + .remove_pmkid = wpa_driver_ralink_remove_pmkid, + .flush_pmkid = wpa_driver_ralink_flush_pmkid, +}; diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h new file mode 100644 index 0000000000000..ddf44de232a3b --- /dev/null +++ b/src/drivers/driver_ralink.h @@ -0,0 +1,382 @@ +/* + * WPA Supplicant - driver_ralink exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * 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. + */ + +// Ralink defined OIDs +#if WIRELESS_EXT <= 11 +#ifndef SIOCDEVPRIVATE +#define SIOCDEVPRIVATE 0x8BE0 +#endif +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif + +#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E) +#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) + +// IEEE 802.11 OIDs & Ralink defined OIDs ****** + +// (RaConfig Set/QueryInform) ==> +#define OID_GET_SET_TOGGLE 0x8000 + +#define OID_802_11_ADD_WEP 0x0112 +#define OID_802_11_REMOVE_WEP 0x0113 +#define OID_802_11_DISASSOCIATE 0x0114 +#define OID_802_11_PRIVACY_FILTER 0x0118 +#define OID_802_11_ASSOCIATION_INFORMATION 0x011E +#define OID_802_11_BSSID_LIST_SCAN 0x0508 +#define OID_802_11_SSID 0x0509 +#define OID_802_11_BSSID 0x050A +#define OID_802_11_WEP_STATUS 0x0510 +#define OID_802_11_AUTHENTICATION_MODE 0x0511 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0512 +#define OID_802_11_TX_POWER_LEVEL 0x0517 +#define OID_802_11_REMOVE_KEY 0x0519 +#define OID_802_11_ADD_KEY 0x0520 +#define OID_802_11_DEAUTHENTICATION 0x0526 +#define OID_802_11_DROP_UNENCRYPTED 0x0527 +#define OID_802_11_BSSID_LIST 0x0609 +#define OID_802_3_CURRENT_ADDRESS 0x060A +#define OID_SET_COUNTERMEASURES 0x0616 +#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode +#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode +#define OID_802_11_PMKID 0x0620 +#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support +#define RT_OID_WE_VERSION_COMPILED 0x0622 +#define RT_OID_NEW_DRIVER 0x0623 + +#define PACKED __attribute__ ((packed)) + +//wpa_supplicant event flags +#define RT_ASSOC_EVENT_FLAG 0x0101 +#define RT_DISASSOC_EVENT_FLAG 0x0102 +#define RT_REQIE_EVENT_FLAG 0x0103 +#define RT_RESPIE_EVENT_FLAG 0x0104 +#define RT_ASSOCINFO_EVENT_FLAG 0x0105 +#define RT_PMKIDCAND_FLAG 0x0106 +#define RT_INTERFACE_DOWN 0x0107 +#define RT_REPORT_AP_INFO 0x0108 + +// +// IEEE 802.11 Structures and definitions +// +// new types for Media Specific Indications + +#ifndef ULONG +#define CHAR char +#define INT int +#define SHORT int +#define UINT u32 +#undef ULONG +//#define ULONG u32 +#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */ +#define USHORT unsigned short +#define UCHAR unsigned char + +#define uint32 u32 +#define uint8 u8 + + +#define BOOLEAN u8 +//#define LARGE_INTEGER s64 +#define VOID void +#define LONG long +#define LONGLONG s64 +#define ULONGLONG u64 +typedef VOID *PVOID; +typedef CHAR *PCHAR; +typedef UCHAR *PUCHAR; +typedef USHORT *PUSHORT; +typedef LONG *PLONG; +typedef ULONG *PULONG; + +typedef union _LARGE_INTEGER { + struct { + ULONG LowPart; + LONG HighPart; + }vv; + struct { + ULONG LowPart; + LONG HighPart; + } u; + s64 QuadPart; +} LARGE_INTEGER; + +#endif + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 +#define MAX_LEN_OF_SSID 32 +#define MAC_ADDR_LEN 6 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +// mask for authentication/integrity fields +#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +// Added new types for OFDM 5G and 2.4G +typedef enum _NDIS_802_11_NETWORK_TYPE +{ + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11Automode, + Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound +} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE; + +// +// Received Signal Strength Indication +// +typedef LONG NDIS_802_11_RSSI; // in dBm + +typedef struct _NDIS_802_11_CONFIGURATION_FH +{ + ULONG Length; // Length of structure + ULONG HopPattern; // As defined by 802.11, MSB set + ULONG HopSet; // to one if non-802.11 + ULONG DwellTime; // units are Kusec +} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH; + +typedef struct _NDIS_802_11_CONFIGURATION +{ + ULONG Length; // Length of structure + ULONG BeaconPeriod; // units are Kusec + ULONG ATIMWindow; // units are Kusec + ULONG DSConfig; // Frequency, units are kHz + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +// Key mapping keys require a BSSID +typedef struct _NDIS_802_11_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + UINT KeyLength; // length of key in bytes + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; // variable length depending on above field +} NDIS_802_11_KEY, *PNDIS_802_11_KEY; + +typedef struct _NDIS_802_11_REMOVE_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY; + +typedef struct PACKED _NDIS_802_11_WEP +{ + UINT Length; // Length of this structure + UINT KeyIndex; // 0 is the per-client key, 1-N are the + // global keys + UINT KeyLength; // length of key in bytes + UCHAR KeyMaterial[1];// variable length depending on above field +} NDIS_802_11_WEP, *PNDIS_802_11_WEP; + + +typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE +{ + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax // Not a real value, defined as upper bound +} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE; + +// PMKID Structures +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct _BSSID_INFO +{ + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO, *PBSSID_INFO; + +typedef struct _NDIS_802_11_PMKID +{ + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID; + +//Added new types for PMKID Candidate lists. +typedef struct _PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE, *PPMKID_CANDIDATE; + +typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST +{ + ULONG Version; // Version of the structure + ULONG NumCandidates; // No. of pmkid candidates + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST; + +//Flags for PMKID Candidate list structure +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +// Add new authentication modes +typedef enum _NDIS_802_11_AUTHENTICATION_MODE +{ + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax // Not a real mode, defined as upper bound +} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates + +typedef struct PACKED _NDIS_802_11_SSID +{ + INT SsidLength; // length of SSID field below, in bytes; + // this can be zero. + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field +} NDIS_802_11_SSID, *PNDIS_802_11_SSID; + + +typedef struct PACKED _NDIS_WLAN_BSSID +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + ULONG Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES SupportedRates; +} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID Bssid[1]; +} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST; + +// Added Capabilities, IELength and IEs for each BSSID +typedef struct PACKED _NDIS_WLAN_BSSID_EX +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + UINT Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX; + +typedef struct PACKED _NDIS_802_11_FIXED_IEs +{ + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs; + +// Added new encryption types +// Also aliased typedef to new name +typedef enum _NDIS_802_11_WEP_STATUS +{ + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS, + NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS; + +typedef enum _NDIS_802_11_RELOAD_DEFAULTS +{ + Ndis802_11ReloadWEPKeys +} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS; + +#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 +#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 +#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 + +#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 +#define NDIS_802_11_AI_RESFI_STATUSCODE 2 +#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 + +typedef struct _NDIS_802_11_AI_REQFI +{ + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI; + +typedef struct _NDIS_802_11_AI_RESFI +{ + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI; + +typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION +{ + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION; + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +typedef struct _MLME_DEAUTH_REQ_STRUCT { + UCHAR Addr[MAC_ADDR_LEN]; + USHORT Reason; +} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT; diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c new file mode 100644 index 0000000000000..4394ddb69ce5f --- /dev/null +++ b/src/drivers/driver_roboswitch.c @@ -0,0 +1,444 @@ +/* + * WPA Supplicant - roboswitch driver interface + * Copyright (c) 2008-2009 Jouke Witteveen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include <linux/mii.h> + +#include "common.h" +#include "driver.h" + +#define ROBO_PHY_ADDR 0x1E /* RoboSwitch PHY address */ + +/* MII access registers */ +#define ROBO_MII_PAGE 0x10 /* MII page register */ +#define ROBO_MII_ADDR 0x11 /* MII address register */ +#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ + +#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ +#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ +#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ +#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ +#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ + +/* Page numbers */ +#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ +#define ROBO_VLAN_PAGE 0x34 /* VLAN page */ + +/* ARL control page registers */ +#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ +#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ +#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ +#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ +#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ + +/* VLAN page registers */ +#define ROBO_VLAN_ACCESS 0x06 /* VLAN table Access register */ +#define ROBO_VLAN_ACCESS_5365 0x08 /* VLAN table Access register (5365) */ +#define ROBO_VLAN_READ 0x0C /* VLAN read register */ +#define ROBO_VLAN_MAX 0xFF /* Maximum number of VLANs */ + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_roboswitch_data { + void *ctx; + char ifname[IFNAMSIZ + 1]; + struct ifreq ifr; + int fd; + u16 ports; +}; + + +/* Copied from the kernel-only part of mii.h. */ +static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} + + +static u16 wpa_driver_roboswitch_mdio_read( + struct wpa_driver_roboswitch_data *drv, u8 reg) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + + if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIREG]"); + return 0x00; + } + return mii->val_out; +} + + +static void wpa_driver_roboswitch_mdio_write( + struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + mii->val_in = val; + + if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCSMIIREG"); + } +} + + +static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u8 op) +{ + int i; + + /* set page number */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, + (page << 8) | ROBO_MII_PAGE_ENABLE); + /* set register address */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); + + /* check if operation completed */ + for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { + if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) + == 0) + return 0; + } + /* timeout */ + return -1; +} + + +static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX || + wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) + return -1; + + for (i = 0; i < len; ++i) { + val[i] = wpa_driver_roboswitch_mdio_read( + drv, ROBO_MII_DATA_OFFSET + i); + } + + return 0; +} + + +static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX) return -1; + for (i = 0; i < len; ++i) { + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, + val[i]); + } + return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); +} + + +static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static const char * wpa_driver_roboswitch_get_ifname(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + return drv->ifname; +} + + +static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, + const u8 *addr) +{ + int i; + u16 _read, zero = 0; + /* For reasons of simplicity we assume ETH_ALEN is even. */ + u16 addr_word[ETH_ALEN / 2]; + /* RoboSwitch uses 16-bit Big Endian addresses. */ + /* The ordering of the words is reversed in the MII registers. */ + for (i = 0; i < ETH_ALEN; i += 2) + addr_word[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); + + /* check if multiport addresses are not yet enabled */ + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, 1) < 0) + return -1; + + if (!(_read & (1 << 4))) { + _read |= 1 << 4; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_word, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &drv->ports, + 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &zero, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, 1); + return 0; + } + + /* check if multiport address 1 is free */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + &_read, 1); + if (_read == 0) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_word, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &drv->ports, + 1); + return 0; + } + /* check if multiport address 2 is free */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, + &_read, 1); + if (_read == 0) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_word, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &drv->ports, + 1); + return 0; + } + + /* out of free multiport addresses */ + return -1; +} + + +static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, + const u8 *addr) +{ + int i; + u16 _read[3], zero = 0; + u16 addr_word[ETH_ALEN / 2]; /* same as at join */ + + for (i = 0; i < ETH_ALEN; i += 2) + addr_word[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); + + /* check if multiport address 1 was used */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + _read, 1); + if (_read[0] == drv->ports) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, _read, 3); + if (os_memcmp(_read, addr_word, 6) == 0) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &zero, + 1); + goto clean_up; + } + } + + /* check if multiport address 2 was used */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, + _read, 1); + if (_read[0] == drv->ports) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, _read, 3); + if (os_memcmp(_read, addr_word, 6) == 0) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &zero, + 1); + goto clean_up; + } + } + + /* used multiport address not found */ + return -1; + +clean_up: + /* leave the multiport registers in a sane state */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + _read, 1); + if (_read[0] == 0) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, _read, 1); + if (_read[0] == 0) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, _read, + 1); + _read[0] &= ~(1 << 4); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, _read, + 1); + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, _read, + 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, _read, + 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, _read, + 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, _read, + 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &zero, + 1); + } + } + return 0; +} + + +static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) +{ + struct wpa_driver_roboswitch_data *drv; + int len = -1, sep = -1; + u16 vlan_max = ROBO_VLAN_MAX, vlan = 0, vlan_read[2]; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) return NULL; + drv->ctx = ctx; + + while (ifname[++len]) { + if (ifname[len] == '.') + sep = len; + } + if (sep < 0 || sep >= len - 1) { + wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in " + "interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + if (sep > IFNAMSIZ) { + wpa_printf(MSG_INFO, "%s: Interface name %s is too long", + __func__, ifname); + os_free(drv); + return NULL; + } + os_memcpy(drv->ifname, ifname, sep); + drv->ifname[sep] = '\0'; + while (++sep < len) { + if (ifname[sep] < '0' || ifname[sep] > '9') { + wpa_printf(MSG_INFO, "%s: Invalid vlan specification " + "in interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + vlan *= 10; + vlan += ifname[sep] - '0'; + if (vlan > ROBO_VLAN_MAX) { + wpa_printf(MSG_INFO, "%s: VLAN out of range in " + "interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + } + + drv->fd = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->fd < 0) { + wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); + os_free(drv); + return NULL; + } + + os_memset(&drv->ifr, 0, sizeof(drv->ifr)); + os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIPHY]"); + os_free(drv); + return NULL; + } + if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { + wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " + "RoboSwitch?)", __func__); + os_free(drv); + return NULL; + } + + /* set the read bit */ + vlan |= 1 << 13; + /* set and read back to see if the register can be used */ + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS, + &vlan_max, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS, + &vlan_max, 1); + if (vlan_max == ROBO_VLAN_MAX) /* pre-5365 */ + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + ROBO_VLAN_ACCESS, &vlan, 1); + else /* 5365 uses a different register */ + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + ROBO_VLAN_ACCESS_5365, &vlan, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, + vlan_read, 2); + if (!(vlan_read[1] & (1 << 4))) { + wpa_printf(MSG_INFO, "%s: Could not get port information for " + "VLAN %d", __func__, vlan & ~(1 << 13)); + os_free(drv); + return NULL; + } + drv->ports = vlan_read[0] & 0x001F; + /* add the MII port */ + drv->ports |= 1 << 8; + if (wpa_driver_roboswitch_join(drv, pae_group_addr) < 0) { + wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " + "RoboSwitch ARL", __func__); + } + + return drv; +} + + +static void wpa_driver_roboswitch_deinit(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (wpa_driver_roboswitch_leave(drv, pae_group_addr) < 0) { + wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", + __func__); + } + + close(drv->fd); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_roboswitch_ops = { + .name = "roboswitch", + .desc = "wpa_supplicant roboswitch driver", + .get_ssid = wpa_driver_roboswitch_get_ssid, + .get_bssid = wpa_driver_roboswitch_get_bssid, + .init = wpa_driver_roboswitch_init, + .deinit = wpa_driver_roboswitch_deinit, + .get_ifname = wpa_driver_roboswitch_get_ifname, +}; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c new file mode 100644 index 0000000000000..2a41cf260a5a9 --- /dev/null +++ b/src/drivers/driver_test.c @@ -0,0 +1,1230 @@ +/* + * WPA Supplicant - testing driver interface + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/* Make dure we get winsock2.h for Windows build to get sockaddr_storage */ +#include "build_config.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock2.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/un.h> +#include <dirent.h> +#include <sys/stat.h> +#define DRIVER_TEST_UNIX +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "sha1.h" +#include "ieee802_11_defs.h" + + +struct wpa_driver_test_global { + int dummy; +}; + +struct wpa_driver_test_data { + struct wpa_driver_test_global *global; + void *ctx; + u8 own_addr[ETH_ALEN]; + int test_socket; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un hostapd_addr; +#endif /* DRIVER_TEST_UNIX */ + int hostapd_addr_set; + struct sockaddr_in hostapd_addr_udp; + int hostapd_addr_udp_set; + char *own_socket_path; + char *test_dir; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; +#define MAX_SCAN_RESULTS 30 + struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; + size_t num_scanres; + int use_associnfo; + u8 assoc_wpa_ie[80]; + size_t assoc_wpa_ie_len; + int use_mlme; + int associated; + u8 *probe_req_ie; + size_t probe_req_ie_len; +}; + + +static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + +#ifdef DRIVER_TEST_UNIX + if (drv->associated && drv->hostapd_addr_set) { + struct stat st; + if (stat(drv->hostapd_addr.sun_path, &st) < 0) { + wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", + __func__, strerror(errno)); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } +#endif /* DRIVER_TEST_UNIX */ + + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); +} + + +static int wpa_driver_test_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return 0; +} + + +static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +#ifdef DRIVER_TEST_UNIX +static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, + const char *path) +{ + struct dirent *dent; + DIR *dir; + struct sockaddr_un addr; + char cmd[512], *pos, *end; + int ret; + + dir = opendir(path); + if (dir == NULL) + return; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + if (drv->probe_req_ie) { + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, + drv->probe_req_ie_len); + } + end[-1] = '\0'; + + while ((dent = readdir(dir))) { + if (os_strncmp(dent->d_name, "AP-", 3) != 0) + continue; + wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + path, dent->d_name); + + if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("sendto(test_socket)"); + } + } + closedir(dir); +} +#endif /* DRIVER_TEST_UNIX */ + + +static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + + drv->num_scanres = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && drv->test_dir) + wpa_driver_scan_dir(drv, drv->test_dir); + + if (drv->test_socket >= 0 && drv->hostapd_addr_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + } + + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + struct wpa_scan_results *res; + size_t i; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *r; + if (drv->scanres[i] == NULL) + continue; + r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); + if (r == NULL) + break; + os_memcpy(r, drv->scanres[i], + sizeof(*r) + drv->scanres[i]->ie_len); + res->res[res->num++] = r; + } + + return res; +} + + +static int wpa_driver_test_set_key(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) +{ + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + if (addr) { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + } + if (seq) { + wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); + } + if (key) { + wpa_hexdump(MSG_DEBUG, " key", key, key_len); + } + return 0; +} + + +static int wpa_driver_test_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " bssid=" MACSTR, + MAC2STR(params->bssid)); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " ssid", + params->ssid, params->ssid_len); + } + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, " wpa_ie", + params->wpa_ie, params->wpa_ie_len); + drv->assoc_wpa_ie_len = params->wpa_ie_len; + if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) + drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); + os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, + drv->assoc_wpa_ie_len); + } else + drv->assoc_wpa_ie_len = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_dir && params->bssid) { + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_snprintf(drv->hostapd_addr.sun_path, + sizeof(drv->hostapd_addr.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(params->bssid)); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && + (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + char cmd[200], *pos, *end; + int ret; + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->ssid, + params->ssid_len); + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, + params->wpa_ie_len); + end[-1] = '\0'; +#ifdef DRIVER_TEST_UNIX + if (drv->hostapd_addr_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->hostapd_addr_udp_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } else { + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + + return 0; +} + + +static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(ssid, drv->ssid, 32); + return drv->ssid_len; +} + + +static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) +{ +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + return 0; +} + + +static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static int wpa_driver_test_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + struct wpa_scan_res *res; + const char *pos, *pos2; + size_t len; + u8 *ie_pos, *ie_start, *ie_end; +#define MAX_IE_LEN 1000 + + wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); + if (drv->num_scanres >= MAX_SCAN_RESULTS) { + wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " + "result"); + return; + } + + /* SCANRESP BSSID SSID IEs */ + + res = os_zalloc(sizeof(*res) + MAX_IE_LEN); + if (res == NULL) + return; + ie_start = ie_pos = (u8 *) (res + 1); + ie_end = ie_pos + MAX_IE_LEN; + + if (hwaddr_aton(data, res->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); + os_free(res); + return; + } + + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " + "in scanres"); + os_free(res); + return; + } + len = (pos2 - pos) / 2; + if (len > 32) + len = 32; + /* + * Generate SSID IE from the SSID field since this IE is not included + * in the main IE field. + */ + *ie_pos++ = WLAN_EID_SSID; + *ie_pos++ = len; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); + os_free(res); + return; + } + ie_pos += len; + + pos = pos2 + 1; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + len = os_strlen(pos) / 2; + else + len = (pos2 - pos) / 2; + if ((int) len > ie_end - ie_pos) + len = ie_end - ie_pos; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); + os_free(res); + return; + } + ie_pos += len; + res->ie_len = ie_pos - ie_start; + + if (pos2) { + pos = pos2 + 1; + while (*pos == ' ') + pos++; + if (os_strncmp(pos, "PRIVACY", 7) == 0) + res->caps |= IEEE80211_CAP_PRIVACY; + } + + os_free(drv->scanres[drv->num_scanres]); + drv->scanres[drv->num_scanres++] = res; +} + + +static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + /* ASSOCRESP BSSID <res> */ + if (hwaddr_aton(data, drv->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " + "assocresp"); + } + if (drv->use_associnfo) { + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = drv->assoc_wpa_ie; + event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); + } + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen) +{ + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + const u8 *src = drv->bssid; + + if (data_len > 14) { + /* Skip Ethernet header */ + src = data + ETH_ALEN; + data += 14; + data_len -= 14; + } + wpa_supplicant_rx_eapol(drv->ctx, src, data, data_len); +} + + +static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status rx_status; + os_memset(&rx_status, 0, sizeof(rx_status)); + wpa_supplicant_sta_rx(drv->ctx, data, data_len, &rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + char *buf; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + os_free(buf); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (os_strncmp(buf, "SCANRESP ", 9) == 0) { + wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, + fromlen, buf + 9); + } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { + wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, + fromlen, buf + 10); + } else if (os_strcmp(buf, "DISASSOC") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strcmp(buf, "DEAUTH") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { + wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 6, res - 6); + } else if (os_strncmp(buf, "MLME ", 5) == 0) { + wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } + os_free(buf); +} + + +static void * wpa_driver_test_init2(void *ctx, const char *ifname, + void *global_priv) +{ + struct wpa_driver_test_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->global = global_priv; + drv->ctx = ctx; + drv->test_socket = -1; + + /* Set dummy BSSID and SSID for testing. */ + drv->bssid[0] = 0x02; + drv->bssid[1] = 0x00; + drv->bssid[2] = 0x00; + drv->bssid[3] = 0x00; + drv->bssid[4] = 0x00; + drv->bssid[5] = 0x01; + os_memcpy(drv->ssid, "test", 5); + drv->ssid_len = 4; + + /* Generate a MAC address to help testing with multiple STAs */ + drv->own_addr[0] = 0x02; /* locally administered */ + sha1_prf((const u8 *) ifname, os_strlen(ifname), + "wpa_supplicant test mac addr generation", + NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); + + return drv; +} + + +static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) +{ + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + drv->test_socket = -1; + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + } +} + + +static void wpa_driver_test_deinit(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + int i; + wpa_driver_test_close_test_socket(drv); + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); + os_free(drv->test_dir); + for (i = 0; i < MAX_SCAN_RESULTS; i++) + os_free(drv->scanres[i]); + os_free(drv->probe_req_ie); + os_free(drv); +} + + +static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, + const char *dir) +{ +#ifdef DRIVER_TEST_UNIX + static unsigned int counter = 0; + struct sockaddr_un addr; + size_t len; + + os_free(drv->own_socket_path); + if (dir) { + len = os_strlen(dir) + 30; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, len, "%s/STA-" MACSTR, + dir, MAC2STR(drv->own_addr)); + } else { + drv->own_socket_path = os_malloc(100); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, 100, + "/tmp/wpa_supplicant_test-%d-%d", + getpid(), counter++); + } + + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->test_socket, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + close(drv->test_socket); + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +#else /* DRIVER_TEST_UNIX */ + return -1; +#endif /* DRIVER_TEST_UNIX */ +} + + +static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, + char *dst) +{ + char *pos; + + pos = os_strchr(dst, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); + + drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_INET)"); + return -1; + } + + os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); + drv->hostapd_addr_udp.sin_family = AF_INET; +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); + + drv->hostapd_addr_udp_set = 1; + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +} + + +static int wpa_driver_test_set_param(void *priv, const char *param) +{ + struct wpa_driver_test_data *drv = priv; + const char *pos; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + return 0; + + wpa_driver_test_close_test_socket(drv); + +#ifdef DRIVER_TEST_UNIX + pos = os_strstr(param, "test_socket="); + if (pos) { + const char *pos2; + size_t len; + + pos += 12; + pos2 = os_strchr(pos, ' '); + if (pos2) + len = pos2 - pos; + else + len = os_strlen(pos); + if (len > sizeof(drv->hostapd_addr.sun_path)) + return -1; + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_memcpy(drv->hostapd_addr.sun_path, pos, len); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + pos = os_strstr(param, "test_dir="); + if (pos) { + char *end; + os_free(drv->test_dir); + drv->test_dir = os_strdup(pos + 9); + if (drv->test_dir == NULL) + return -1; + end = os_strchr(drv->test_dir, ' '); + if (end) + *end = '\0'; + if (wpa_driver_test_attach(drv, drv->test_dir)) + return -1; + } else { + pos = os_strstr(param, "test_udp="); + if (pos) { + char *dst, *epos; + dst = os_strdup(pos + 9); + if (dst == NULL) + return -1; + epos = os_strchr(dst, ' '); + if (epos) + *epos = '\0'; + if (wpa_driver_test_attach_udp(drv, dst)) + return -1; + os_free(dst); + } else if (wpa_driver_test_attach(drv, NULL)) + return -1; + } + + if (os_strstr(param, "use_associnfo=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); + drv->use_associnfo = 1; + } + +#ifdef CONFIG_CLIENT_MLME + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME"); + drv->use_mlme = 1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +static const u8 * wpa_driver_test_get_mac_addr(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + char *msg; + size_t msg_len; + struct l2_ethhdr eth; + struct sockaddr *addr; + socklen_t alen; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un addr_un; +#endif /* DRIVER_TEST_UNIX */ + + wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); + + os_memset(ð, 0, sizeof(eth)); + os_memcpy(eth.h_dest, dest, ETH_ALEN); + os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + msg_len = 6 + sizeof(eth) + data_len; + msg = os_malloc(msg_len); + if (msg == NULL) + return -1; + os_memcpy(msg, "EAPOL ", 6); + os_memcpy(msg + 6, ð, sizeof(eth)); + os_memcpy(msg + 6 + sizeof(eth), data, data_len); + + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + addr = (struct sockaddr *) &drv->hostapd_addr_udp; + alen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + addr = (struct sockaddr *) &drv->hostapd_addr; + alen = sizeof(drv->hostapd_addr); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + } else { +#ifdef DRIVER_TEST_UNIX + struct stat st; + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr_un.sun_path, &st) < 0) { + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + + if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { + perror("sendmsg(test_socket)"); + os_free(msg); + return -1; + } + + os_free(msg); + return 0; +} + + +static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_test_data *drv = priv; + os_memset(capa, 0, sizeof(*capa)); + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | + WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + if (drv->use_mlme) + capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + return 0; +} + + +static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, + int protect_type, + int key_type) +{ + wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", + __func__, protect_type, key_type); + + if (addr) { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, + __func__, MAC2STR(addr)); + } + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME +static struct wpa_hw_modes * +wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_hw_modes *modes; + + *num_modes = 1; + *flags = 0; + modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); + if (modes == NULL) + return NULL; + modes[0].mode = WPA_MODE_IEEE80211G; + modes[0].num_channels = 1; + modes[0].num_rates = 1; + modes[0].channels = os_zalloc(sizeof(struct wpa_channel_data)); + modes[0].rates = os_zalloc(sizeof(struct wpa_rate_data)); + if (modes[0].channels == NULL || modes[0].rates == NULL) { + wpa_supplicant_sta_free_hw_features(modes, *num_modes); + return NULL; + } + modes[0].channels[0].chan = 1; + modes[0].channels[0].freq = 2412; + modes[0].channels[0].flag = WPA_CHAN_W_SCAN | WPA_CHAN_W_ACTIVE_SCAN; + modes[0].rates[0].rate = 10; + modes[0].rates[0].flags = WPA_RATE_BASIC | WPA_RATE_SUPPORTED | + WPA_RATE_CCK | WPA_RATE_MANDATORY; + + return modes; +} + + +static int wpa_driver_test_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", + __func__, phymode, chan, freq); + return 0; +} + + +static int wpa_driver_test_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + struct msghdr msg; + struct iovec io[2]; + struct sockaddr_un addr; + const u8 *dest; + struct dirent *dent; + DIR *dir; + + wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); + if (data_len < 10) + return -1; + dest = data + 4; + + io[0].iov_base = "MLME "; + io[0].iov_len = 5; + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + msg.msg_name = &drv->hostapd_addr_udp; + msg.msg_namelen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + msg.msg_name = &drv->hostapd_addr; + msg.msg_namelen = sizeof(drv->hostapd_addr); +#endif /* DRIVER_TEST_UNIX */ + } + } else if (os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + { + dir = opendir(drv->test_dir); + if (dir == NULL) + return -1; + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", + __func__, dent->d_name); + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/%s", drv->test_dir, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + + if (sendmsg(drv->test_socket, &msg, 0) < 0) + perror("sendmsg(test_socket)"); + } + closedir(dir); + return 0; + } else { + struct stat st; + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr.sun_path, &st) < 0) { + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/STA-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + } + + if (sendmsg(drv->test_socket, &msg, 0) < 0) { + perror("sendmsg(test_socket)"); + return -1; + } + + return 0; +} + + +static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + return 0; +} + + +static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) +{ + wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_test_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_test_data *drv = priv; + + os_free(drv->probe_req_ie); + if (ies) { + drv->probe_req_ie = os_malloc(ies_len); + if (drv->probe_req_ie == NULL) { + drv->probe_req_ie_len = 0; + return -1; + } + os_memcpy(drv->probe_req_ie, ies, ies_len); + drv->probe_req_ie_len = ies_len; + } else { + drv->probe_req_ie = NULL; + drv->probe_req_ie_len = 0; + } + return 0; +} + + +static void * wpa_driver_test_global_init(void) +{ + struct wpa_driver_test_global *global; + + global = os_zalloc(sizeof(*global)); + return global; +} + + +static void wpa_driver_test_global_deinit(void *priv) +{ + struct wpa_driver_test_global *global = priv; + os_free(global); +} + + +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) +{ + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return iface; + iface->ifname = os_strdup("sta0"); + iface->desc = os_strdup("test interface 0"); + iface->drv_name = "test"; + iface->next = os_zalloc(sizeof(*iface)); + if (iface->next) { + iface->next->ifname = os_strdup("sta1"); + iface->next->desc = os_strdup("test interface 1"); + iface->next->drv_name = "test"; + } + + return iface; +} + + +const struct wpa_driver_ops wpa_driver_test_ops = { + "test", + "wpa_supplicant test driver", + wpa_driver_test_get_bssid, + wpa_driver_test_get_ssid, + wpa_driver_test_set_wpa, + wpa_driver_test_set_key, + NULL /* init */, + wpa_driver_test_deinit, + wpa_driver_test_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_test_scan, + NULL /* get_scan_results */, + wpa_driver_test_deauthenticate, + wpa_driver_test_disassociate, + wpa_driver_test_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_test_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_test_get_mac_addr, + wpa_driver_test_send_eapol, + NULL /* set_operstate */, + wpa_driver_test_mlme_setprotection, +#ifdef CONFIG_CLIENT_MLME + wpa_driver_test_get_hw_feature_data, + wpa_driver_test_set_channel, + wpa_driver_test_set_ssid, + wpa_driver_test_set_bssid, + wpa_driver_test_send_mlme, + wpa_driver_test_mlme_add_sta, + wpa_driver_test_mlme_remove_sta, +#else /* CONFIG_CLIENT_MLME */ + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, +#endif /* CONFIG_CLIENT_MLME */ + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_test_get_scan_results2, + wpa_driver_test_set_probe_req_ie, + NULL /* set_mode */, + NULL /* set_country */, + wpa_driver_test_global_init, + wpa_driver_test_global_deinit, + wpa_driver_test_init2, + wpa_driver_test_get_interfaces +}; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c new file mode 100644 index 0000000000000..8c2daa8276604 --- /dev/null +++ b/src/drivers/driver_wext.c @@ -0,0 +1,2349 @@ +/* + * WPA Supplicant - driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a driver interface for the Linux Wireless Extensions. + * When used with WE-18 or newer, this interface can be used as-is with number + * of drivers. In addition to this, some of the common functions in this file + * can be used by other driver interface implementations that use generic WE + * ioctls, but require private ioctls for some of the functionality. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" + + +static int wpa_driver_wext_flush_pmkid(void *priv); +static int wpa_driver_wext_get_range(void *priv); +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); + + +static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: Buffer for BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +/** + * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Buffer for the SSID; must be at least 32 bytes long + * Returns: SSID length on success, -1 on failure + */ +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: SSID + * @ssid_len: Length of SSID (0..32) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_freq(void *priv, int freq) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_wext_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_pmkidcand( + struct wpa_driver_wext_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocreqie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocrespie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_wext_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVMICHAELMICFAILURE length"); + return; + } + wpa_driver_wext_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVCUSTOM length"); + return; + } + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_wext_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case SIOCGIWSCAN: + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, + drv, ctx); + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case IWEVASSOCREQIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCREQIE length"); + return; + } + wpa_driver_wext_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCRESPIE length"); + return; + } + wpa_driver_wext_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVPMKIDCAND length"); + return; + } + wpa_driver_wext_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_wext_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " + "interface"); + wpa_driver_wext_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_wext_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_wext_send_oper_ifla(drv, -1, IF_OPER_UP); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_wext_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_wext private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags) +{ + return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_wext private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) +{ + return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_wext_init - Initialize WE driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +void * wpa_driver_wext_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_nl local; + struct wpa_driver_wext_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + os_free(drv); + return NULL; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx); + drv->event_sock = s; + + drv->mlme_sock = -1; + + wpa_driver_wext_finish_drv_init(drv); + + return drv; +} + + +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +{ + int flags; + + if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } else { + /* + * Wait some time to allow driver to initialize before + * starting configuring the driver. This seems to be + * needed at least some drivers that load firmware etc. + * when the interface is set up. + */ + wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting " + "a second for the driver to complete " + "initialization", drv->ifname); + sleep(1); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_wext_flush_pmkid(drv); + + if (wpa_driver_wext_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + wpa_driver_wext_get_range(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + if (os_strncmp(drv->ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. Since some of the versions included WE-18 + * support, let's add the alternative ifindex also from + * driver_wext.c for the time being. This may be removed at + * some point once it is believed that old versions of the + * driver are not in use anymore. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv, ifname2); + } + + wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_wext_deinit - Deinitialize WE driver interface + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_wext_init(). + */ +void wpa_driver_wext_deinit(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + int flags; + + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + (void) wpa_driver_wext_set_bssid(drv, + (u8 *) "\x00\x00\x00\x00\x00\x00"); + + wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->event_sock); + if (drv->mlme_sock >= 0) + eloop_unregister_read_sock(drv->mlme_sock); + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->event_sock); + close(drv->ioctl_sock); + if (drv->mlme_sock >= 0) + close(drv->mlme_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + os_free(drv); +} + + +/** + * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_wext_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_wext_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @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 + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0, timeout; + struct iw_scan_req req; + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ssid && ssid_len) { + os_memset(&req, 0, sizeof(req)); + req.essid_len = ssid_len; + req.bssid.sa_family = ARPHRD_ETHER; + os_memset(req.bssid.sa_data, 0xff, ETH_ALEN); + os_memcpy(req.essid, ssid, ssid_len); + iwr.u.data.pointer = (caddr_t) &req; + iwr.u.data.length = sizeof(req); + iwr.u.data.flags = IW_SCAN_THIS_ESSID; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 5; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); + + return ret; +} + + +static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, + size_t *len) +{ + struct iwreq iwr; + u8 *res_buf; + size_t res_buf_len; + + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return NULL; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 65535) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + if (res_buf_len > 65535) + res_buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return NULL; + } + } + + if (iwr.u.data.length > res_buf_len) { + os_free(res_buf); + return NULL; + } + *len = iwr.u.data.length; + + return res_buf; +} + + +/* + * Data structure for collecting WEXT scan results. This is needed to allow + * the various methods of reporting IEs to be combined into a single IE buffer. + */ +struct wext_scan_data { + struct wpa_scan_res res; + u8 *ie; + size_t ie_len; + u8 ssid[32]; + size_t ssid_len; + int maxrate; +}; + + +static void wext_get_scan_mode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (iwe->u.mode == IW_MODE_ADHOC) + res->res.caps |= IEEE80211_CAP_IBSS; + else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA) + res->res.caps |= IEEE80211_CAP_ESS; +} + + +static void wext_get_scan_ssid(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + int ssid_len = iwe->u.essid.length; + if (custom + ssid_len > end) + return; + if (iwe->u.essid.flags && + ssid_len > 0 && + ssid_len <= IW_ESSID_MAX_SIZE) { + os_memcpy(res->ssid, custom, ssid_len); + res->ssid_len = ssid_len; + } +} + + +static void wext_get_scan_freq(struct iw_event *iwe, + struct wext_scan_data *res) +{ + int divi = 1000000, i; + + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report frequency, but a channel. + * Try to map this to frequency by assuming they are using + * IEEE 802.11b/g. But don't overwrite a previously parsed + * frequency if the driver sends both frequency and channel, + * since the driver may be sending an A-band channel that we + * don't handle here. + */ + + if (res->res.freq) + return; + + if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { + res->res.freq = 2407 + 5 * iwe->u.freq.m; + return; + } else if (iwe->u.freq.m == 14) { + res->res.freq = 2484; + return; + } + } + + if (iwe->u.freq.e > 6) { + wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID=" + MACSTR " m=%d e=%d)", + MAC2STR(res->res.bssid), iwe->u.freq.m, + iwe->u.freq.e); + return; + } + + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + res->res.freq = iwe->u.freq.m / divi; +} + + +static void wext_get_scan_qual(struct iw_event *iwe, + struct wext_scan_data *res) +{ + res->res.qual = iwe->u.qual.qual; + res->res.noise = iwe->u.qual.noise; + res->res.level = iwe->u.qual.level; +} + + +static void wext_get_scan_encode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (!(iwe->u.data.flags & IW_ENCODE_DISABLED)) + res->res.caps |= IEEE80211_CAP_PRIVACY; +} + + +static void wext_get_scan_rate(struct iw_event *iwe, + struct wext_scan_data *res, char *pos, + char *end) +{ + int maxrate; + char *custom = pos + IW_EV_LCP_LEN; + struct iw_param p; + size_t clen; + + clen = iwe->len; + if (custom + clen > end) + return; + maxrate = 0; + while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { + /* Note: may be misaligned, make a local, aligned copy */ + os_memcpy(&p, custom, sizeof(struct iw_param)); + if (p.value > maxrate) + maxrate = p.value; + clen -= sizeof(struct iw_param); + custom += sizeof(struct iw_param); + } + + /* Convert the maxrate from WE-style (b/s units) to + * 802.11 rates (500000 b/s units). + */ + res->maxrate = maxrate / 500000; +} + + +static void wext_get_scan_iwevgenie(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + char *genie, *gpos, *gend; + u8 *tmp; + + if (iwe->u.data.length == 0) + return; + + gpos = genie = custom; + gend = genie + iwe->u.data.length; + if (gend > end) { + wpa_printf(MSG_INFO, "IWEVGENIE overflow"); + return; + } + + tmp = os_realloc(res->ie, res->ie_len + gend - gpos); + if (tmp == NULL) + return; + os_memcpy(tmp + res->ie_len, gpos, gend - gpos); + res->ie = tmp; + res->ie_len += gend - gpos; +} + + +static void wext_get_scan_custom(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + size_t clen; + u8 *tmp; + + clen = iwe->u.data.length; + if (custom + clen > end) + return; + + if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { + char *spos; + int bytes; + u8 bin[8]; + spos = custom + 4; + bytes = custom + clen - spos; + if (bytes != 16) { + wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes); + return; + } + bytes /= 2; + hexstr2bin(spos, bin, bytes); + res->res.tsf += WPA_GET_BE64(bin); + } +} + + +static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd) +{ + return drv->we_version_compiled > 18 && + (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE || + cmd == IWEVGENIE || cmd == IWEVCUSTOM); +} + + +static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, + struct wext_scan_data *data) +{ + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + size_t extra_len; + u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL; + + /* Figure out whether we need to fake any IEs */ + pos = data->ie; + end = pos + data->ie_len; + while (pos && pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_SSID) + ssid_ie = pos; + else if (pos[0] == WLAN_EID_SUPP_RATES) + rate_ie = pos; + else if (pos[0] == WLAN_EID_EXT_SUPP_RATES) + rate_ie = pos; + pos += 2 + pos[1]; + } + + extra_len = 0; + if (ssid_ie == NULL) + extra_len += 2 + data->ssid_len; + if (rate_ie == NULL && data->maxrate) + extra_len += 3; + + r = os_zalloc(sizeof(*r) + extra_len + data->ie_len); + if (r == NULL) + return; + os_memcpy(r, &data->res, sizeof(*r)); + r->ie_len = extra_len + data->ie_len; + pos = (u8 *) (r + 1); + if (ssid_ie == NULL) { + /* + * Generate a fake SSID IE since the driver did not report + * a full IE list. + */ + *pos++ = WLAN_EID_SSID; + *pos++ = data->ssid_len; + os_memcpy(pos, data->ssid, data->ssid_len); + pos += data->ssid_len; + } + if (rate_ie == NULL && data->maxrate) { + /* + * Generate a fake Supported Rates IE since the driver did not + * report a full IE list. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = data->maxrate; + } + if (data->ie) + os_memcpy(pos, data->ie, data->ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return; + } + tmp[res->num++] = r; + res->res = tmp; +} + + +/** + * wpa_driver_wext_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + size_t ap_num = 0, len; + int first; + u8 *res_buf; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom; + struct wpa_scan_results *res; + struct wext_scan_data data; + + res_buf = wpa_driver_wext_giwscan(drv, &len); + if (res_buf == NULL) + return NULL; + + ap_num = 0; + first = 1; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(res_buf); + return NULL; + } + + pos = (char *) res_buf; + end = (char *) res_buf + len; + os_memset(&data, 0, sizeof(data)); + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (wext_19_iw_point(drv, iwe->cmd)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + first = 0; + os_free(data.ie); + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.res.bssid, + iwe->u.ap_addr.sa_data, ETH_ALEN); + break; + case SIOCGIWMODE: + wext_get_scan_mode(iwe, &data); + break; + case SIOCGIWESSID: + wext_get_scan_ssid(iwe, &data, custom, end); + break; + case SIOCGIWFREQ: + wext_get_scan_freq(iwe, &data); + break; + case IWEVQUAL: + wext_get_scan_qual(iwe, &data); + break; + case SIOCGIWENCODE: + wext_get_scan_encode(iwe, &data); + break; + case SIOCGIWRATE: + wext_get_scan_rate(iwe, &data, pos, end); + break; + case IWEVGENIE: + wext_get_scan_iwevgenie(iwe, &data, custom, end); + break; + case IWEVCUSTOM: + wext_get_scan_custom(iwe, &data, custom, end); + break; + } + + pos += iwe->len; + } + os_free(res_buf); + res_buf = NULL; + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + os_free(data.ie); + + wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", + (unsigned long) len, (unsigned long) res->num); + + return res; +} + + +static int wpa_driver_wext_get_range(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " + "flags 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_wext_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, + const u8 *psk) +{ + struct iw_encode_ext *ext; + struct iwreq iwr; + int ret; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return 0; + + if (!psk) + return 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + ext = os_zalloc(sizeof(*ext) + PMK_LEN); + if (ext == NULL) + return -1; + + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + PMK_LEN; + ext->key_len = PMK_LEN; + os_memcpy(&ext->key, psk, ext->key_len); + ext->alg = IW_ENCODE_ALG_PMK; + + ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); + if (ret < 0) + perror("ioctl[SIOCSIWENCODEEXT] PMK"); + os_free(ext); + + return ret; +} + + +static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", + __FUNCTION__, (unsigned long) seq_len); + return -1; + } + + ext = os_zalloc(sizeof(*ext) + key_len); + if (ext == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + key_len; + + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; + if (set_tx) + ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; + + ext->addr.sa_family = ARPHRD_ETHER; + if (addr) + os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); + if (key && key_len) { + os_memcpy(ext + 1, key, key_len); + ext->key_len = key_len; + } + switch (alg) { + case WPA_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + break; + case WPA_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + case WPA_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + case WPA_ALG_CCMP: + ext->alg = IW_ENCODE_ALG_CCMP; + break; + case WPA_ALG_PMK: + ext->alg = IW_ENCODE_ALG_PMK; + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + ext->alg = IW_ENCODE_ALG_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", + __FUNCTION__, alg); + os_free(ext); + return -1; + } + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + os_memcpy(ext->rx_seq, seq, seq_len); + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = errno == EOPNOTSUPP ? -2 : -1; + if (errno == ENODEV) { + /* + * ndiswrapper seems to be returning incorrect error + * code.. */ + ret = -2; + } + + perror("ioctl[SIOCSIWENCODEEXT]"); + } + + os_free(ext); + return ret; +} + + +/** + * wpa_driver_wext_set_key - Configure encryption key + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @priv: Private driver interface data + * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys + * @set_tx: Configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: Sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: Length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: Length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * Returns: 0 on success, -1 on failure + * + * This function uses SIOCSIWENCODEEXT by default, but tries to use + * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. + */ +int wpa_driver_wext_set_key(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) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", + __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + if (ret == 0) + return 0; + + if (ret == -2 && + (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); + ret = 0; + } else { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT"); + return ret; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) key; + iwr.u.encoding.length = key_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE] (set_tx)"); + ret = -1; + } + } + + return ret; +} + + +static int wpa_driver_wext_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_wext_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_wext_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) +{ + const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 ssid[32]; + int i; + + /* + * Clear the BSSID selection and set a random SSID to make sure the + * driver will not be trying to associate with something even if it + * does not understand SIOCSIWMLME commands (or tries to associate + * automatically after deauth/disassoc). + */ + wpa_driver_wext_set_bssid(drv, null_bssid); + + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xFF; + wpa_driver_wext_set_ssid(drv, ssid, 32); +} + + +static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + wpa_driver_wext_disconnect(drv); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); + return ret; +} + + +static int wpa_driver_wext_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +int wpa_driver_wext_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_wext_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_wext_set_psk(drv, params->psk) < 0) + ret = -1; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + switch (params->mgmt_frame_protection) { + case NO_MGMT_FRAME_PROTECTION: + value = IW_AUTH_MFP_DISABLED; + break; + case MGMT_FRAME_PROTECTION_OPTIONAL: + value = IW_AUTH_MFP_OPTIONAL; + break; + case MGMT_FRAME_PROTECTION_REQUIRED: + value = IW_AUTH_MFP_REQUIRED; + break; + }; + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_wext_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_mode(void *priv, int mode) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = -1, flags; + unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) { + ret = 0; + goto done; + } + + if (errno != EBUSY) { + perror("ioctl[SIOCSIWMODE]"); + goto done; + } + + /* mac80211 doesn't allow mode changes while the device is up, so if + * the device isn't in the mode we're about to change to, take device + * down, try to set the mode again, and bring it back up. + */ + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + goto done; + } + + if (iwr.u.mode == new_mode) { + ret = 0; + goto done; + } + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) + perror("ioctl[SIOCSIWMODE]"); + else + ret = 0; + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_wext_get_ifflags(drv, &flags); + (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP); + } + +done: + return ret; +} + + +static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_wext_flush_pmkid(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_wext_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname) +{ + if (ifname == NULL) { + drv->ifindex2 = -1; + return 0; + } + + drv->ifindex2 = if_nametoindex(ifname); + if (drv->ifindex2 <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " + "wireless events", drv->ifindex2, ifname); + + return 0; +} + + +int wpa_driver_wext_set_operstate(void *priv, int state) +{ + struct wpa_driver_wext_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_wext_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) +{ + return drv->we_version_compiled; +} + + +const struct wpa_driver_ops wpa_driver_wext_ops = { + .name = "wext", + .desc = "Linux wireless extensions (generic)", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .set_wpa = wpa_driver_wext_set_wpa, + .set_key = wpa_driver_wext_set_key, + .set_countermeasures = wpa_driver_wext_set_countermeasures, + .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .deauthenticate = wpa_driver_wext_deauthenticate, + .disassociate = wpa_driver_wext_disassociate, + .set_mode = wpa_driver_wext_set_mode, + .associate = wpa_driver_wext_associate, + .set_auth_alg = wpa_driver_wext_set_auth_alg, + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .add_pmkid = wpa_driver_wext_add_pmkid, + .remove_pmkid = wpa_driver_wext_remove_pmkid, + .flush_pmkid = wpa_driver_wext_flush_pmkid, + .get_capa = wpa_driver_wext_get_capa, + .set_operstate = wpa_driver_wext_set_operstate, +}; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h new file mode 100644 index 0000000000000..b89c2cb2fdfa8 --- /dev/null +++ b/src/drivers/driver_wext.h @@ -0,0 +1,82 @@ +/* + * WPA Supplicant - driver_wext exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DRIVER_WEXT_H +#define DRIVER_WEXT_H + +#include <net/if.h> + +struct wpa_driver_wext_data { + void *ctx; + int event_sock; + int ioctl_sock; + int mlme_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int ifindex2; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; +}; + +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags); +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags); +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); +int wpa_driver_wext_set_freq(void *priv, int freq); +int wpa_driver_wext_set_mode(void *priv, int mode); +int wpa_driver_wext_set_key(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); +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len); +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); + +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname); + +void * wpa_driver_wext_init(void *ctx, const char *ifname); +void wpa_driver_wext_deinit(void *priv); + +int wpa_driver_wext_set_operstate(void *priv, int state); +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv); + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params); +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa); +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value); +int wpa_driver_wext_cipher2wext(int cipher); +int wpa_driver_wext_keymgmt2wext(int keymgmt); + +#endif /* DRIVER_WEXT_H */ diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c new file mode 100644 index 0000000000000..83cb3f99cbaa5 --- /dev/null +++ b/src/drivers/driver_wired.c @@ -0,0 +1,277 @@ +/* + * WPA Supplicant - wired Ethernet driver interface + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#ifdef __linux__ +#include <netpacket/packet.h> +#endif /* __linux__ */ +#ifdef __FreeBSD__ +#include <net/if_dl.h> +#endif /* __FreeBSD__ */ + +#include "common.h" +#include "driver.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_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +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; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#ifdef __FreeBSD__ + { + struct sockaddr_dl *dlp; + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* __FreeBSD__ */ + + 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; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = if_nametoindex(drv->ifname); + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_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 = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + +#ifdef __linux__ + drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->pf_sock < 0) + perror("socket(PF_PACKET)"); +#else /* __linux__ */ + drv->pf_sock = -1; +#endif /* __linux__ */ + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (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__); + os_free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } + + 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); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "wpa_supplicant wired Ethernet driver", + .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/src/drivers/drivers.c b/src/drivers/drivers.c new file mode 100644 index 0000000000000..d278797d7de2f --- /dev/null +++ b/src/drivers/drivers.c @@ -0,0 +1,139 @@ +/* + * WPA Supplicant / driver interface list + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + + +#ifdef CONFIG_DRIVER_WEXT +extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NL80211 +extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 +extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES +extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL +extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_NDISWRAPPER +/* driver_ndiswrapper.c */ +extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops; +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM +extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW +extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */ +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST +extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_RALINK +extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */ +#endif /* CONFIG_DRIVER_RALINK */ +#ifdef CONFIG_DRIVER_OSX +extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */ +#endif /* CONFIG_DRIVER_OSX */ +#ifdef CONFIG_DRIVER_PS3 +extern struct wpa_driver_ops wpa_driver_ps3_ops; /* driver_ps3.c */ +#endif /* CONFIG_DRIVER_PS3 */ +#ifdef CONFIG_DRIVER_IPHONE +extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ +#endif /* CONFIG_DRIVER_IPHONE */ +#ifdef CONFIG_DRIVER_ROBOSWITCH +/* driver_roboswitch.c */ +extern struct wpa_driver_ops wpa_driver_roboswitch_ops; +#endif /* CONFIG_DRIVER_ROBOSWITCH */ + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ +#ifdef CONFIG_DRIVER_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NL80211 + &wpa_driver_nl80211_ops, +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 + &wpa_driver_prism54_ops, +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES + &wpa_driver_hermes_ops, +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL + &wpa_driver_atmel_ops, +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_NDISWRAPPER + &wpa_driver_ndiswrapper_ops, +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM + &wpa_driver_broadcom_ops, +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW + &wpa_driver_ipw_ops, +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS + &wpa_driver_ndis_ops, +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED + &wpa_driver_wired_ops, +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST + &wpa_driver_test_ops, +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_RALINK + &wpa_driver_ralink_ops, +#endif /* CONFIG_DRIVER_RALINK */ +#ifdef CONFIG_DRIVER_OSX + &wpa_driver_osx_ops, +#endif /* CONFIG_DRIVER_OSX */ +#ifdef CONFIG_DRIVER_PS3 + &wpa_driver_ps3_ops, +#endif /* CONFIG_DRIVER_PS3 */ +#ifdef CONFIG_DRIVER_IPHONE + &wpa_driver_iphone_ops, +#endif /* CONFIG_DRIVER_IPHONE */ +#ifdef CONFIG_DRIVER_ROBOSWITCH + &wpa_driver_roboswitch_ops, +#endif /* CONFIG_DRIVER_ROBOSWITCH */ + NULL +}; diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c new file mode 100644 index 0000000000000..f6eaa7c9f7ee2 --- /dev/null +++ b/src/drivers/ndis_events.c @@ -0,0 +1,808 @@ +/* + * ndis_events - Receive NdisMIndicateStatus() events using WMI + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#define _WIN32_WINNT 0x0400 + +#include "includes.h" + +#ifndef COBJMACROS +#define COBJMACROS +#endif /* COBJMACROS */ +#include <wbemidl.h> + +#include "common.h" + + +static int wmi_refcnt = 0; +static int wmi_first = 1; + +struct ndis_events_data { + IWbemObjectSink sink; + IWbemObjectSinkVtbl sink_vtbl; + + IWbemServices *pSvc; + IWbemLocator *pLoc; + + HANDLE read_pipe, write_pipe, event_avail; + UINT ref; + int terminating; + char *ifname; /* {GUID..} */ + WCHAR *adapter_desc; +}; + +#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL +#define BstrFree(x) if (x) SysFreeString(x) + +/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to + * BSTRs */ +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, + pCtx, ppEnum); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, + bsQuery, lFlags, pCtx, + pResponseHandler); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( + IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, + LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, + LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) +{ + BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; + HRESULT hr; + + bsNetworkResource = BstrAlloc(strNetworkResource); + bsUser = BstrAlloc(strUser); + bsPassword = BstrAlloc(strPassword); + bsLocale = BstrAlloc(strLocale); + bsAuthority = BstrAlloc(strAuthority); + + hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, + bsPassword, bsLocale, lSecurityFlags, + bsAuthority, pCtx, ppNamespace); + + BstrFree(bsNetworkResource); + BstrFree(bsUser); + BstrFree(bsPassword); + BstrFree(bsLocale); + BstrFree(bsAuthority); + + return hr; +} + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, + EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc); + + +static int ndis_events_constructor(struct ndis_events_data *events) +{ + events->ref = 1; + + if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { + wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", + (int) GetLastError()); + return -1; + } + events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (events->event_avail == NULL) { + wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", + (int) GetLastError()); + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + return -1; + } + + return 0; +} + + +static void ndis_events_destructor(struct ndis_events_data *events) +{ + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + CloseHandle(events->event_avail); + IWbemServices_Release(events->pSvc); + IWbemLocator_Release(events->pLoc); + if (--wmi_refcnt == 0) + CoUninitialize(); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) +{ + *obj = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IWbemObjectSink)) { + *obj = this; + IWbemObjectSink_AddRef(this); + return NOERROR; + } + + return E_NOINTERFACE; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + return ++events->ref; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + + if (--events->ref != 0) + return events->ref; + + ndis_events_destructor(events); + wpa_printf(MSG_DEBUG, "ndis_events: terminated"); + os_free(events->adapter_desc); + os_free(events->ifname); + os_free(events); + return 0; +} + + +static int ndis_events_send_event(struct ndis_events_data *events, + enum event_types type, + char *data, size_t data_len) +{ + char buf[512], *pos, *end; + int _type; + DWORD written; + + end = buf + sizeof(buf); + _type = (int) type; + os_memcpy(buf, &_type, sizeof(_type)); + pos = buf + sizeof(_type); + + if (data) { + if (2 + data_len > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "Not enough room for send_event " + "data (%d)", data_len); + return -1; + } + *pos++ = data_len >> 8; + *pos++ = data_len & 0xff; + os_memcpy(pos, data, data_len); + pos += data_len; + } + + if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { + SetEvent(events->event_avail); + return 0; + } + wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); + return -1; +} + + +static void ndis_events_media_connect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); + ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); +} + + +static void ndis_events_media_disconnect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); + ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); +} + + +static void ndis_events_media_specific(struct ndis_events_data *events, + IWbemClassObject *pObj) +{ + VARIANT vt; + HRESULT hr; + LONG lower, upper, k; + UCHAR ch; + char *data, *pos; + size_t data_len; + + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); + + /* This is the StatusBuffer from NdisMIndicateStatus() call */ + hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", + 0, &vt, NULL, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Could not get " + "NdisStatusMediaSpecificIndication from " + "the object?!"); + return; + } + + SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); + SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); + data_len = upper - lower + 1; + data = os_malloc(data_len); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " + "data"); + VariantClear(&vt); + return; + } + + pos = data; + for (k = lower; k <= upper; k++) { + SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); + *pos++ = ch; + } + wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); + + VariantClear(&vt); + + ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); + + os_free(data); +} + + +static void ndis_events_adapter_arrival(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); + ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); +} + + +static void ndis_events_adapter_removal(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); + ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_indicate(IWbemObjectSink *this, long lObjectCount, + IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + long i; + + if (events->terminating) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication - terminating"); + return WBEM_NO_ERROR; + } + /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", + lObjectCount); */ + + for (i = 0; i < lObjectCount; i++) { + IWbemClassObject *pObj = ppObjArray[i]; + HRESULT hr; + VARIANT vtClass, vt; + + hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " + "event."); + break; + } + /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ + + hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get InstanceName " + "from event."); + VariantClear(&vtClass); + break; + } + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " + "update adapter description since it may " + "have changed with new adapter instance"); + ndis_events_get_adapter(events, events->ifname, NULL); + } + + if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication for foreign adapter: " + "InstanceName: '%S' __CLASS: '%S'", + vt.bstrVal, vtClass.bstrVal); + VariantClear(&vtClass); + VariantClear(&vt); + continue; + } + VariantClear(&vt); + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaSpecificIndication") == 0) { + ndis_events_media_specific(events, pObj); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaConnect") == 0) { + ndis_events_media_connect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaDisconnect") == 0) { + ndis_events_media_disconnect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + ndis_events_adapter_arrival(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterRemoval") == 0) { + ndis_events_adapter_removal(events); + } else { + wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " + "'%S'", vtClass.bstrVal); + } + + VariantClear(&vtClass); + } + + return WBEM_NO_ERROR; +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, + BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) +{ + return WBEM_NO_ERROR; +} + + +static int notification_query(IWbemObjectSink *pDestSink, + IWbemServices *pSvc, const char *class_name) +{ + HRESULT hr; + WCHAR query[256]; + + _snwprintf(query, 256, + L"SELECT * FROM %S", class_name); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + hr = call_IWbemServices_ExecNotificationQueryAsync( + pSvc, L"WQL", query, 0, 0, pDestSink); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " + "failed with hresult of 0x%x", + class_name, (int) hr); + return -1; + } + + return 0; +} + + +static int register_async_notification(IWbemObjectSink *pDestSink, + IWbemServices *pSvc) +{ + int i; + const char *class_list[] = { + "MSNdis_StatusMediaConnect", + "MSNdis_StatusMediaDisconnect", + "MSNdis_StatusMediaSpecificIndication", + "MSNdis_NotifyAdapterArrival", + "MSNdis_NotifyAdapterRemoval", + NULL + }; + + for (i = 0; class_list[i]; i++) { + if (notification_query(pDestSink, pSvc, class_list[i]) < 0) + return -1; + } + + return 0; +} + + +void ndis_events_deinit(struct ndis_events_data *events) +{ + events->terminating = 1; + IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); + IWbemObjectSink_Release(&events->sink); + /* + * Rest of deinitialization is done in ndis_events_destructor() once + * all reference count drops to zero. + */ +} + + +static int ndis_events_use_desc(struct ndis_events_data *events, + const char *desc) +{ + char *tmp, *pos; + size_t len; + + if (desc == NULL) { + if (events->adapter_desc == NULL) + return -1; + /* Continue using old description */ + return 0; + } + + tmp = os_strdup(desc); + if (tmp == NULL) + return -1; + + pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); + if (pos) + *pos = '\0'; + + len = os_strlen(tmp); + events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); + if (events->adapter_desc == NULL) { + os_free(tmp); + return -1; + } + _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); + os_free(tmp); + return 0; +} + + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemServices *pSvc; +#define MAX_QUERY_LEN 256 + WCHAR query[MAX_QUERY_LEN]; + IEnumWbemClassObject *pEnumerator; + IWbemClassObject *pObj; + ULONG uReturned; + VARIANT vt; + int len, pos; + + /* + * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter + * to have better probability of matching with InstanceName from + * MSNdis events. If this fails, use the provided description. + */ + + os_free(events->adapter_desc); + events->adapter_desc = NULL; + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " + "server (ROOT\\CIMV2) - error 0x%x", (int) hr); + return ndis_events_use_desc(events, desc); + } + wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Index FROM Win32_NetworkAdapterConfiguration " + L"WHERE SettingID='%S'", ifname); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + VariantInit(&vt); + hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " + "Win32_NetworkAdapterConfiguration: 0x%x", + (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " + L"Index=%d", + vt.uintVal); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + VariantClear(&vt); + IWbemClassObject_Release(pObj); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", + vt.bstrVal); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + /* + * Try to get even better candidate for matching with InstanceName + * from Win32_PnPEntity. This is needed at least for some USB cards + * that can change the InstanceName whenever being unplugged and + * plugged again. + */ + + hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" + "'%S'", vt.bstrVal); + + len = _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); + if (len < 0 || len >= MAX_QUERY_LEN - 1) { + VariantClear(&vt); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + /* Escape \ as \\ */ + for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { + if (vt.bstrVal[pos] == '\\') { + if (len >= MAX_QUERY_LEN - 3) + break; + query[len++] = '\\'; + } + query[len++] = vt.bstrVal[pos]; + } + query[len++] = L'\''; + query[len] = L'\0'; + VariantClear(&vt); + IWbemClassObject_Release(pObj); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "Name from Win32_PnPEntity: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_PnPEntity: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_PnPEntity: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", + vt.bstrVal); + os_free(events->adapter_desc); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + IWbemClassObject_Release(pObj); + + IWbemServices_Release(pSvc); + + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + + return 0; +} + + +struct ndis_events_data * +ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemObjectSink *pSink; + struct ndis_events_data *events; + + events = os_zalloc(sizeof(*events)); + if (events == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate sink for events."); + return NULL; + } + events->ifname = os_strdup(ifname); + if (events->ifname == NULL) { + os_free(events); + return NULL; + } + + if (wmi_refcnt++ == 0) { + hr = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " + "returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + if (wmi_first) { + /* CoInitializeSecurity() must be called once and only once + * per process, so let's use wmi_first flag to protect against + * multiple calls. */ + wmi_first = 0; + + hr = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_SECURE_REFS, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " + "- returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) (void *) &events->pLoc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events); + return NULL; + } + + if (ndis_events_get_adapter(events, ifname, desc) < 0) { + CoUninitialize(); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", + events->adapter_desc); + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\WMI", NULL, NULL, + 0, 0, 0, 0, &events->pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "Could not connect to server - error " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); + + ndis_events_constructor(events); + pSink = &events->sink; + pSink->lpVtbl = &events->sink_vtbl; + events->sink_vtbl.QueryInterface = ndis_events_query_interface; + events->sink_vtbl.AddRef = ndis_events_add_ref; + events->sink_vtbl.Release = ndis_events_release; + events->sink_vtbl.Indicate = ndis_events_indicate; + events->sink_vtbl.SetStatus = ndis_events_set_status; + + if (register_async_notification(pSink, events->pSvc) < 0) { + wpa_printf(MSG_DEBUG, "Failed to register async " + "notifications"); + ndis_events_destructor(events); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + + *read_pipe = events->read_pipe; + *event_avail = events->event_avail; + + return events; +} diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h new file mode 100644 index 0000000000000..2a31e251a0138 --- /dev/null +++ b/src/drivers/priv_netlink.h @@ -0,0 +1,104 @@ +/* + * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif +#ifndef IFLA_OPERSTATE +#define IFLA_OPERSTATE 16 +#endif +#ifndef IFLA_LINKMODE +#define IFLA_LINKMODE 17 +#define IF_OPER_DORMANT 5 +#define IF_OPER_UP 6 +#endif + +#define NLM_F_REQUEST 1 + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) +#define RTM_SETLINK (RTM_BASE + 3) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/src/drivers/radiotap.c b/src/drivers/radiotap.c new file mode 100644 index 0000000000000..804473fa4bfb9 --- /dev/null +++ b/src/drivers/radiotap.c @@ -0,0 +1,287 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * + * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> + * I only modified some things on top to ease syncing should bugs be found. + */ + +#include "includes.h" + +#include "common.h" +#include "radiotap_iter.h" + +#define le16_to_cpu le_to_host16 +#define le32_to_cpu le_to_host32 +#define __le32 uint32_t +#define ulong unsigned long +#define unlikely(cond) (cond) +#define get_unaligned(p) \ +({ \ + struct packed_dummy_struct { \ + typeof(*(p)) __val; \ + } __attribute__((packed)) *__ptr = (void *) (p); \ + \ + __ptr->__val; \ +}) + +/* function prototypes and related defs are in radiotap_iter.h */ + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header. It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code. Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member. This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length) +{ + /* Linux only supports version 0 radiotap format */ + if (radiotap_header->it_version) + return -EINVAL; + + /* sanity check for allowed length and radiotap length field */ + if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) + return -EINVAL; + + iterator->rtheader = radiotap_header; + iterator->max_length = le16_to_cpu(get_unaligned( + &radiotap_header->it_len)); + iterator->arg_index = 0; + iterator->bitmap_shifter = le32_to_cpu(get_unaligned( + &radiotap_header->it_present)); + iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); + iterator->this_arg = NULL; + + /* find payload start allowing for extended bitmap(s) */ + + if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { + while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & + (1<<IEEE80211_RADIOTAP_EXT)) { + iterator->arg += sizeof(u32); + + /* + * check for insanity where the present bitmaps + * keep claiming to extend up to or even beyond the + * stated radiotap header length + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) + > (ulong)iterator->max_length) + return -EINVAL; + } + + iterator->arg += sizeof(u32); + + /* + * no need to check again for blowing past stated radiotap + * header length, because ieee80211_radiotap_iterator_next + * checks it before it is dereferenced + */ + } + + /* we are all initialized happily */ + + return 0; +} + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field. It takes care of alignment handling and extended + * present fields. @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator) +{ + + /* + * small length lookup table for all radiotap types we heard of + * starting from b0 in the bitmap, so we can walk the payload + * area of the radiotap header + * + * There is a requirement to pad args, so that args + * of a given length must begin at a boundary of that length + * -- but note that compound args are allowed (eg, 2 x u16 + * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not + * a reliable indicator of alignment requirement. + * + * upper nybble: content alignment for arg + * lower nybble: content length for arg + */ + + static const u8 rt_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = 0x88, + [IEEE80211_RADIOTAP_FLAGS] = 0x11, + [IEEE80211_RADIOTAP_RATE] = 0x11, + [IEEE80211_RADIOTAP_CHANNEL] = 0x24, + [IEEE80211_RADIOTAP_FHSS] = 0x22, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, + [IEEE80211_RADIOTAP_ANTENNA] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, + [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, + /* + * add more here as they are defined in + * include/net/ieee80211_radiotap.h + */ + }; + + /* + * for every radiotap entry we can at + * least skip (by knowing the length)... + */ + + while (iterator->arg_index < (int) sizeof(rt_sizes)) { + int hit = 0; + int pad; + + if (!(iterator->bitmap_shifter & 1)) + goto next_entry; /* arg not present */ + + /* + * arg is present, account for alignment padding + * 8-bit args can be at any alignment + * 16-bit args must start on 16-bit boundary + * 32-bit args must start on 32-bit boundary + * 64-bit args must start on 64-bit boundary + * + * note that total arg size can differ from alignment of + * elements inside arg, so we use upper nybble of length + * table to base alignment on + * + * also note: these alignments are ** relative to the + * start of the radiotap header **. There is no guarantee + * that the radiotap header itself is aligned on any + * kind of boundary. + * + * the above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area + */ + + pad = (((ulong)iterator->arg) - + ((ulong)iterator->rtheader)) & + ((rt_sizes[iterator->arg_index] >> 4) - 1); + + if (pad) + iterator->arg += + (rt_sizes[iterator->arg_index] >> 4) - pad; + + /* + * this is what we will return to user, but we need to + * move on first so next call has something fresh to test + */ + iterator->this_arg_index = iterator->arg_index; + iterator->this_arg = iterator->arg; + hit = 1; + + /* internally move on the size of this arg */ + iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + + /* + * check for insanity where we are given a bitmap that + * claims to have more arg content than the length of the + * radiotap section. We will normally end up equalling this + * max_length on the last arg, never exceeding it. + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) > + (ulong) iterator->max_length) + return -EINVAL; + + next_entry: + iterator->arg_index++; + if (unlikely((iterator->arg_index & 31) == 0)) { + /* completed current u32 bitmap */ + if (iterator->bitmap_shifter & 1) { + /* b31 was set, there is more */ + /* move to next u32 bitmap */ + iterator->bitmap_shifter = le32_to_cpu( + get_unaligned(iterator->next_bitmap)); + iterator->next_bitmap++; + } else + /* no more bitmaps: end */ + iterator->arg_index = sizeof(rt_sizes); + } else /* just try the next bit */ + iterator->bitmap_shifter >>= 1; + + /* if we found a valid arg earlier, return it now */ + if (hit) + return 0; + } + + /* we don't know how to handle any more args, we're done */ + return -ENOENT; +} diff --git a/src/drivers/radiotap.h b/src/drivers/radiotap.h new file mode 100644 index 0000000000000..508264c4cf333 --- /dev/null +++ b/src/drivers/radiotap.h @@ -0,0 +1,242 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include <stdint.h> + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { + uint8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + uint8_t it_pad; + uint16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + uint32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +}; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS uint16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/drivers/radiotap_iter.h b/src/drivers/radiotap_iter.h new file mode 100644 index 0000000000000..92a798a67023a --- /dev/null +++ b/src/drivers/radiotap_iter.h @@ -0,0 +1,41 @@ +#ifndef __RADIOTAP_ITER_H +#define __RADIOTAP_ITER_H + +#include "radiotap.h" + +/* Radiotap header iteration + * implemented in radiotap.c + */ +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @rtheader: pointer to the radiotap header we are walking through + * @max_length: length of radiotap header in cpu byte ordering + * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg + * @this_arg: pointer to current radiotap arg + * @arg_index: internal next argument index + * @arg: internal next argument pointer + * @next_bitmap: internal pointer to next present u32 + * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + */ + +struct ieee80211_radiotap_iterator { + struct ieee80211_radiotap_header *rtheader; + int max_length; + int this_arg_index; + unsigned char *this_arg; + + int arg_index; + unsigned char *arg; + uint32_t *next_bitmap; + uint32_t bitmap_shifter; +}; + +extern int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length); + +extern int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator); + +#endif /* __RADIOTAP_ITER_H */ diff --git a/src/drivers/scan_helpers.c b/src/drivers/scan_helpers.c new file mode 100644 index 0000000000000..63387701ea9aa --- /dev/null +++ b/src/drivers/scan_helpers.c @@ -0,0 +1,182 @@ +/* + * WPA Supplicant - Helper functions for scan result processing + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "drivers/driver.h" +#include "ieee802_11_defs.h" + + +const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, + u32 vendor_type) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(res->ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +int wpa_scan_get_max_rate(const struct wpa_scan_res *res) +{ + int rate = 0; + const u8 *ie; + int i; + + ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + return rate; +} + + +void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + +/* Compare function for sorting scan results. Return >0 if @b is considered + * better. */ +static int wpa_scan_result_compar(const void *a, const void *b) +{ + struct wpa_scan_res **_wa = (void *) a; + struct wpa_scan_res **_wb = (void *) b; + struct wpa_scan_res *wa = *_wa; + struct wpa_scan_res *wb = *_wb; + int wpa_a, wpa_b, maxrate_a, maxrate_b; + + /* WPA/WPA2 support preferred */ + wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || + wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; + wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || + wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; + + if (wpa_b && !wpa_a) + return 1; + if (!wpa_b && wpa_a) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAP_PRIVACY)) + return 1; + if ((wa->caps & IEEE80211_CAP_PRIVACY) && + (wb->caps & IEEE80211_CAP_PRIVACY) == 0) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || + (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { + maxrate_a = wpa_scan_get_max_rate(wa); + maxrate_b = wpa_scan_get_max_rate(wb); + if (maxrate_a != maxrate_b) + return maxrate_b - maxrate_a; + } + + /* use freq for channel preference */ + + /* all things being equal, use signal level; if signal levels are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (wb->level == wa->level) + return wb->qual - wa->qual; + return wb->level - wa->level; +} + + +void wpa_scan_sort_results(struct wpa_scan_results *res) +{ + qsort(res->res, res->num, sizeof(struct wpa_scan_res *), + wpa_scan_result_compar); +} diff --git a/src/eap_common/.gitignore b/src/eap_common/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/eap_common/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/eap_common/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/ieee802_11h.c b/src/eap_common/chap.c index 215e377da5521..a088aff64e2d7 100644 --- a/contrib/hostapd/ieee802_11h.c +++ b/src/eap_common/chap.c @@ -1,6 +1,5 @@ /* - * hostapd / IEEE 802.11h - * Copyright (c) 2005-2006, Devicescape Software, Inc. + * CHAP-MD5 (RFC 1994) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -15,20 +14,22 @@ #include "includes.h" -#include "hostapd.h" +#include "common.h" +#include "md5.h" +#include "crypto.h" +#include "chap.h" - -int hostapd_check_power_cap(struct hostapd_data *hapd, u8 *power, u8 len) +void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response) { - unsigned int max_pwr; + const u8 *addr[3]; + size_t len[3]; - if (len < 2) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Too short power capability IE\n"); - return -1; - } - max_pwr = power[1]; - if (max_pwr > hapd->iface->sta_max_power) - return -1; - return 0; + addr[0] = &id; + len[0] = 1; + addr[1] = secret; + len[1] = secret_len; + addr[2] = challenge; + len[2] = challenge_len; + md5_vector(3, addr, len, response); } diff --git a/contrib/hostapd/ieee802_11h.h b/src/eap_common/chap.h index b2bd5497f82f0..209dc8a48f0f6 100644 --- a/contrib/hostapd/ieee802_11h.h +++ b/src/eap_common/chap.h @@ -1,6 +1,5 @@ /* - * hostapd / IEEE 802.11h - * Copyright (c) 2005-2006, Devicescape Software, Inc. + * CHAP-MD5 (RFC 1994) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -13,15 +12,12 @@ * See README and COPYING for more details. */ -#ifndef IEEE802_11H_H -#define IEEE802_11H_H +#ifndef CHAP_H +#define CHAP_H -#define SPECT_LOOSE_BINDING 1 -#define SPECT_STRICT_BINDING 2 +#define CHAP_MD5_LEN 16 -#define CHAN_SWITCH_MODE_NOISY 0 -#define CHAN_SWITCH_MODE_QUIET 1 +void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response); -int hostapd_check_power_cap(struct hostapd_data *hapd, u8 *power, u8 len); - -#endif /* IEEE802_11H_H */ +#endif /* CHAP_H */ diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c new file mode 100644 index 0000000000000..4afa1ddb2acb3 --- /dev/null +++ b/src/eap_common/eap_common.c @@ -0,0 +1,184 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_defs.h" +#include "eap_common.h" + +/** + * eap_hdr_validate - Validate EAP header + * @vendor: Expected EAP Vendor-Id (0 = IETF) + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @plen: Pointer to variable to contain the returned payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function to verify + * that the received EAP request packet has a valid header. This function is + * able to process both legacy and expanded EAP headers and in most cases, the + * caller can just use the returned payload pointer (into *plen) for processing + * the payload regardless of whether the packet used the expanded EAP header or + * not. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return NULL; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return NULL; + } + + pos = (const u8 *) (hdr + 1); + + if (*pos == EAP_TYPE_EXPANDED) { + int exp_vendor; + u32 exp_type; + if (len < sizeof(*hdr) + 8) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " + "length"); + return NULL; + } + pos++; + exp_vendor = WPA_GET_BE24(pos); + pos += 3; + exp_type = WPA_GET_BE32(pos); + pos += 4; + if (exp_vendor != vendor || exp_type != (u32) eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " + "type"); + return NULL; + } + + *plen = len - sizeof(*hdr) - 8; + return pos; + } else { + if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; + } +} + + +/** + * eap_msg_alloc - Allocate a buffer for an EAP message + * @vendor: Vendor-Id (0 = IETF) + * @type: EAP type + * @payload_len: Payload length in bytes (data after Type) + * @code: Message Code (EAP_CODE_*) + * @identifier: Identifier + * Returns: Pointer to the allocated message buffer or %NULL on error + * + * This function can be used to allocate a buffer for an EAP message and fill + * in the EAP header. This function is automatically using expanded EAP header + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do + * not need to separately select which header type to use when using this + * function to allocate the message buffers. The returned buffer has room for + * payload_len bytes and has the EAP header and Type field already filled in. + */ +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + struct wpabuf *buf; + struct eap_hdr *hdr; + size_t len; + + len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + + payload_len; + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + + hdr = wpabuf_put(buf, sizeof(*hdr)); + hdr->code = code; + hdr->identifier = identifier; + hdr->length = host_to_be16(len); + + if (vendor == EAP_VENDOR_IETF) { + wpabuf_put_u8(buf, type); + } else { + wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); + wpabuf_put_be24(buf, vendor); + wpabuf_put_be32(buf, type); + } + + return buf; +} + + +/** + * eap_update_len - Update EAP header length + * @msg: EAP message from eap_msg_alloc + * + * This function updates the length field in the EAP header to match with the + * current length for the buffer. This allows eap_msg_alloc() to be used to + * allocate a larger buffer than the exact message length (e.g., if exact + * message length is not yet known). + */ +void eap_update_len(struct wpabuf *msg) +{ + struct eap_hdr *hdr; + hdr = wpabuf_mhead(msg); + if (wpabuf_len(msg) < sizeof(*hdr)) + return; + hdr->length = host_to_be16(wpabuf_len(msg)); +} + + +/** + * eap_get_id - Get EAP Identifier from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The Identifier field from the EAP header + */ +u8 eap_get_id(const struct wpabuf *msg) +{ + const struct eap_hdr *eap; + + if (wpabuf_len(msg) < sizeof(*eap)) + return 0; + + eap = wpabuf_head(msg); + return eap->identifier; +} + + +/** + * eap_get_id - Get EAP Type from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The EAP Type after the EAP header + */ +EapType eap_get_type(const struct wpabuf *msg) +{ + if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1) + return EAP_TYPE_NONE; + + return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; +} diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h new file mode 100644 index 0000000000000..b95e76b94f5c3 --- /dev/null +++ b/src/eap_common/eap_common.h @@ -0,0 +1,28 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_COMMON_H +#define EAP_COMMON_H + +#include "wpabuf.h" + +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen); +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier); +void eap_update_len(struct wpabuf *msg); +u8 eap_get_id(const struct wpabuf *msg); +EapType eap_get_type(const struct wpabuf *msg); + +#endif /* EAP_COMMON_H */ diff --git a/contrib/hostapd/eap_defs.h b/src/eap_common/eap_defs.h index 8ea923a8f9d06..0ac95b5d7e41b 100644 --- a/contrib/hostapd/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -1,6 +1,6 @@ /* * EAP server/peer: Shared EAP definitions - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,7 +24,7 @@ struct eap_hdr { u8 code; u8 identifier; - u16 length; /* including code and identifier; network byte order */ + be16 length; /* including code and identifier; network byte order */ /* followed by length-4 octets of data */ } STRUCT_PACKED; @@ -38,6 +38,10 @@ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, /* EAP Request and Response data begins with one octet Type. Success and * Failure do not have additional data. */ +/* + * EAP Method Types as allocated by IANA: + * http://www.iana.org/assignments/eap-numbers + */ typedef enum { EAP_TYPE_NONE = 0, EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, @@ -49,24 +53,30 @@ typedef enum { EAP_TYPE_TLS = 13 /* RFC 2716 */, EAP_TYPE_LEAP = 17 /* Cisco proprietary */, EAP_TYPE_SIM = 18 /* RFC 4186 */, - EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */, + EAP_TYPE_TTLS = 21 /* RFC 5281 */, EAP_TYPE_AKA = 23 /* RFC 4187 */, EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, - EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-05.txt */, + EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; + * type 38 has previously been allocated for + * EAP-HTTP Digest, (funk.com) */, + EAP_TYPE_FAST = 43 /* RFC 4851 */, EAP_TYPE_PAX = 46 /* RFC 4746 */, EAP_TYPE_PSK = 47 /* RFC 4764 */, EAP_TYPE_SAKE = 48 /* RFC 4763 */, - EAP_TYPE_EXPANDED = 254 /* RFC 3748 */, - EAP_TYPE_GPSK = 255 /* EXPERIMENTAL - type not yet allocated - * draft-ietf-emu-eap-gpsk-01.txt */ + EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, + EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */, + EAP_TYPE_GPSK = 51 /* draft-ietf-emu-eap-gpsk-17.txt */, + EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; /* SMI Network Management Private Enterprise Code for vendor specific types */ enum { - EAP_VENDOR_IETF = 0 + EAP_VENDOR_IETF = 0, + EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ }; #define EAP_MSK_LEN 64 diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c new file mode 100644 index 0000000000000..4d3deafa0cec9 --- /dev/null +++ b/src/eap_common/eap_fast_common.c @@ -0,0 +1,304 @@ +/* + * EAP-FAST common helper functions (RFC 4851) + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "tls.h" +#include "eap_defs.h" +#include "eap_tlv_common.h" +#include "eap_fast_common.h" + + +void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len) +{ + struct pac_tlv_hdr hdr; + hdr.type = host_to_be16(type); + hdr.len = host_to_be16(len); + wpabuf_put_data(buf, &hdr, sizeof(hdr)); +} + + +void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, + u16 len) +{ + eap_fast_put_tlv_hdr(buf, type, len); + wpabuf_put_data(buf, data, len); +} + + +void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data) +{ + eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data)); + wpabuf_put_buf(buf, data); +} + + +struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + eap_fast_put_tlv_buf(e, + EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV, + buf); + wpabuf_free(buf); + return e; +} + + +void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, + const u8 *client_random, u8 *master_secret) +{ +#define TLS_RANDOM_LEN 32 +#define TLS_MASTER_SECRET_LEN 48 + u8 seed[2 * TLS_RANDOM_LEN]; + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", + client_random, TLS_RANDOM_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", + server_random, TLS_RANDOM_LEN); + + /* + * RFC 4851, Section 5.1: + * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * server_random + client_random, 48) + */ + os_memcpy(seed, server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN); + sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN, + "PAC to master secret label hash", + seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret", + master_secret, TLS_MASTER_SECRET_LEN); +} + + +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, + const char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + int block_size; + + block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); + if (block_size < 0) + return NULL; + + out = os_malloc(block_size + len); + if (out == NULL) + return NULL; + + if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) + == 0) { + os_memmove(out, out + block_size, len); + return out; + } + + if (tls_connection_get_keys(ssl_ctx, conn, &keys)) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + + os_memcpy(rnd, keys.server_random, keys.server_random_len); + os_memcpy(rnd + keys.server_random_len, keys.client_random, + keys.client_random_len); + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " + "expansion", keys.master_key, keys.master_key_len); + if (tls_prf(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, block_size + len)) + goto fail; + os_free(rnd); + os_memmove(out, out + block_size, len); + return out; + +fail: + os_free(rnd); + os_free(out); + return NULL; +} + + +void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) +{ + /* + * RFC 4851, Section 5.4: EAP Master Session Key Generation + * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) + */ + + sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Session Key Generating Function", (u8 *) "", 0, + msk, EAP_FAST_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", + msk, EAP_FAST_KEY_LEN); +} + + +void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) +{ + /* + * RFC 4851, Section 5.4: EAP Master Session Key Genreration + * EMSK = T-PRF(S-IMCK[j], + * "Extended Session Key Generating Function", 64) + */ + + sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Extended Session Key Generating Function", (u8 *) "", 0, + emsk, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", + emsk, EAP_EMSK_LEN); +} + + +int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, + int tlv_type, u8 *pos, int len) +{ + switch (tlv_type) { + case EAP_TLV_EAP_PAYLOAD_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV", + pos, len); + if (tlv->eap_payload_tlv) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "EAP-Payload TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->eap_payload_tlv = pos; + tlv->eap_payload_tlv_len = len; + break; + case EAP_TLV_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len); + if (tlv->result) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Result TLV in the message"); + tlv->result = EAP_TLV_RESULT_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Result TLV"); + tlv->result = EAP_TLV_RESULT_FAILURE; + break; + } + tlv->result = WPA_GET_BE16(pos); + if (tlv->result != EAP_TLV_RESULT_SUCCESS && + tlv->result != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d", + tlv->result); + tlv->result = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", + tlv->result == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_INTERMEDIATE_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV", + pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Intermediate-Result TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + break; + } + if (tlv->iresult) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Intermediate-Result TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->iresult = WPA_GET_BE16(pos); + if (tlv->iresult != EAP_TLV_RESULT_SUCCESS && + tlv->iresult != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate " + "Result %d", tlv->iresult); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s", + tlv->iresult == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV", + pos, len); + if (tlv->crypto_binding) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Crypto-Binding TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; + if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Crypto-Binding TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *) + (pos - sizeof(struct eap_tlv_hdr)); + break; + case EAP_TLV_REQUEST_ACTION_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV", + pos, len); + if (tlv->request_action) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Request-Action TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Request-Action TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + break; + } + tlv->request_action = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d", + tlv->request_action); + break; + case EAP_TLV_PAC_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len); + if (tlv->pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "PAC TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->pac = pos; + tlv->pac_len = len; + break; + default: + /* Unknown TLV */ + return -1; + } + + return 0; +} diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h new file mode 100644 index 0000000000000..257123e1575df --- /dev/null +++ b/src/eap_common/eap_fast_common.h @@ -0,0 +1,117 @@ +/* + * EAP-FAST definitions (RFC 4851) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_FAST_H +#define EAP_FAST_H + +#define EAP_FAST_VERSION 1 +#define EAP_FAST_KEY_LEN 64 +#define EAP_FAST_SIMCK_LEN 40 +#define EAP_FAST_SKS_LEN 40 +#define EAP_FAST_CMK_LEN 20 + +#define TLS_EXT_PAC_OPAQUE 35 + +/* + * draft-cam-winget-eap-fast-provisioning-04.txt: + * Section 4.2.1 - Formats for PAC TLV Attributes / Type Field + * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined + * in the general PAC TLV format (Section 4.2). + */ +#define PAC_TYPE_PAC_KEY 1 +#define PAC_TYPE_PAC_OPAQUE 2 +#define PAC_TYPE_CRED_LIFETIME 3 +#define PAC_TYPE_A_ID 4 +#define PAC_TYPE_I_ID 5 +/* + * 6 was previous assigned for SERVER_PROTECTED_DATA, but + * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved. + */ +#define PAC_TYPE_A_ID_INFO 7 +#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 +#define PAC_TYPE_PAC_INFO 9 +#define PAC_TYPE_PAC_TYPE 10 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct pac_tlv_hdr { + be16 type; + be16 len; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +#define EAP_FAST_PAC_KEY_LEN 32 + +/* draft-cam-winget-eap-fast-provisioning-04.txt: 4.2.6 PAC-Type TLV + * Note: Machine Authentication PAC and User Authorization PAC were removed in + * draft-cam-winget-eap-fast-provisioning-03.txt + */ +#define PAC_TYPE_TUNNEL_PAC 1 +/* Application Specific Short Lived PACs (only in volatile storage) */ +/* User Authorization PAC */ +#define PAC_TYPE_USER_AUTHORIZATION 3 +/* Application Specific Long Lived PACs */ +/* Machine Authentication PAC */ +#define PAC_TYPE_MACHINE_AUTHENTICATION 2 + + +/* + * draft-cam-winget-eap-fast-provisioning-04.txt: + * Section 3.4 - Key Derivations Used in the EAP-FAST Provisioning Exchange + */ +struct eap_fast_key_block_provisioning { + /* Extra key material after TLS key_block */ + u8 session_key_seed[EAP_FAST_SKS_LEN]; + u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */ + u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */ +}; + + +struct wpabuf; +struct tls_connection; + +struct eap_fast_tlv_parse { + u8 *eap_payload_tlv; + size_t eap_payload_tlv_len; + struct eap_tlv_crypto_binding_tlv *crypto_binding; + size_t crypto_binding_len; + int iresult; + int result; + int request_action; + u8 *pac; + size_t pac_len; +}; + +void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len); +void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, + u16 len); +void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data); +struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf); +void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, + const u8 *client_random, u8 *master_secret); +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, + const char *label, size_t len); +void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); +void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); +int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, + int tlv_type, u8 *pos, int len); + +#endif /* EAP_FAST_H */ diff --git a/contrib/hostapd/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c index 7422fa6c48f94..414610cf5a4ed 100644 --- a/contrib/hostapd/eap_gpsk_common.c +++ b/src/eap_common/eap_gpsk_common.c @@ -66,7 +66,8 @@ static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); - omac1_aes_128_vector(psk, 2, addr, vlen, hash); + if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) + return -1; clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; @@ -122,7 +123,7 @@ static int eap_gpsk_derive_keys_helper(u32 csuite_specifier, { u8 mk[32], *pos, *data; size_t data_len, mk_len; - int (*gkdf)(const u8 *psk, const u8 *data, size_t data_len, + int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, u8 *buf, size_t len); gkdf = NULL; @@ -376,8 +377,8 @@ static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, const u8 *data, size_t len, u8 *mic) { if (sk_len != 16) { - wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %d for " - "AES-CMAC MIC", sk_len); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " + "AES-CMAC MIC", (unsigned long) sk_len); return -1; } diff --git a/contrib/hostapd/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h index a30ab97ffa07f..a30ab97ffa07f 100644 --- a/contrib/hostapd/eap_gpsk_common.h +++ b/src/eap_common/eap_gpsk_common.h diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c new file mode 100644 index 0000000000000..e9a9c55eb3018 --- /dev/null +++ b/src/eap_common/eap_ikev2_common.c @@ -0,0 +1,132 @@ +/* + * EAP-IKEv2 common routines + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_defs.h" +#include "eap_common.h" +#include "ikev2_common.h" +#include "eap_ikev2_common.h" + + +int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, + const u8 *i_nonce, size_t i_nonce_len, + const u8 *r_nonce, size_t r_nonce_len, + u8 *keymat) +{ + u8 *nonces; + size_t nlen; + + /* KEYMAT = prf+(SK_d, Ni | Nr) */ + if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL) + return -1; + + nlen = i_nonce_len + r_nonce_len; + nonces = os_malloc(nlen); + if (nonces == NULL) + return -1; + os_memcpy(nonces, i_nonce, i_nonce_len); + os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len); + + if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen, + keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) { + os_free(nonces); + return -1; + } + os_free(nonces); + + wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT", + keymat, EAP_MSK_LEN + EAP_EMSK_LEN); + + return 0; +} + + +struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + +#ifdef CCNS_PL + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + wpabuf_put_u8(msg, 0); /* Flags */ +#else /* CCNS_PL */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " + "for fragment ack"); + return NULL; + } +#endif /* CCNS_PL */ + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack"); + + return msg; +} + + +int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, + int initiator, const struct wpabuf *msg, + const u8 *pos, const u8 *end) +{ + const struct ikev2_integ_alg *integ; + size_t icv_len; + u8 icv[IKEV2_MAX_HASH_LEN]; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + integ = ikev2_get_integ(integ_alg); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot validate ICV"); + return -1; + } + icv_len = integ->hash_len; + + if (end - pos < (int) icv_len) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the " + "message for Integrity Checksum Data"); + return -1; + } + + if (SK_a == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation"); + return -1; + } + + if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len, + wpabuf_head(msg), + wpabuf_len(msg) - icv_len, icv) < 0) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV"); + return -1; + } + + if (os_memcmp(icv, end - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV"); + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV", + icv, icv_len); + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV", + end - icv_len, icv_len); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in " + "the received message"); + + return icv_len; +} diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h new file mode 100644 index 0000000000000..a9fc2caae7269 --- /dev/null +++ b/src/eap_common/eap_ikev2_common.h @@ -0,0 +1,42 @@ +/* + * EAP-IKEv2 definitions + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_IKEV2_COMMON_H +#define EAP_IKEV2_COMMON_H + +#ifdef CCNS_PL +/* incorrect bit order */ +#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01 +#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02 +#define IKEV2_FLAGS_ICV_INCLUDED 0x04 +#else /* CCNS_PL */ +#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80 +#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40 +#define IKEV2_FLAGS_ICV_INCLUDED 0x20 +#endif /* CCNS_PL */ + +#define IKEV2_FRAGMENT_SIZE 1400 + +struct ikev2_keys; + +int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, + const u8 *i_nonce, size_t i_nonce_len, + const u8 *r_nonce, size_t r_nonce_len, + u8 *keymat); +struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code); +int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, + int initiator, const struct wpabuf *msg, + const u8 *pos, const u8 *end); + +#endif /* EAP_IKEV2_COMMON_H */ diff --git a/contrib/hostapd/eap_pax_common.c b/src/eap_common/eap_pax_common.c index 80110469dcc38..80110469dcc38 100644 --- a/contrib/hostapd/eap_pax_common.c +++ b/src/eap_common/eap_pax_common.c diff --git a/contrib/hostapd/eap_pax_common.h b/src/eap_common/eap_pax_common.h index bbad5e4052ff7..dcc171ec2c0c4 100644 --- a/contrib/hostapd/eap_pax_common.h +++ b/src/eap_common/eap_pax_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-PAX shared routines - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,10 +20,6 @@ #endif /* _MSC_VER */ 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; diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c new file mode 100644 index 0000000000000..14625f9639218 --- /dev/null +++ b/src/eap_common/eap_peap_common.c @@ -0,0 +1,88 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "eap_peap_common.h" + +void peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } +} diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h new file mode 100644 index 0000000000000..f59afb07d098d --- /dev/null +++ b/src/eap_common/eap_peap_common.h @@ -0,0 +1,22 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PEAP_COMMON_H +#define EAP_PEAP_COMMON_H + +void peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len); + +#endif /* EAP_PEAP_COMMON_H */ diff --git a/contrib/hostapd/eap_psk_common.c b/src/eap_common/eap_psk_common.c index 8d896ae639d5a..0def3e885371c 100644 --- a/contrib/hostapd/eap_psk_common.c +++ b/src/eap_common/eap_psk_common.c @@ -22,43 +22,53 @@ #define aes_block_size 16 -void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) { os_memset(ak, 0, aes_block_size); - aes_128_encrypt_block(psk, ak, ak); + if (aes_128_encrypt_block(psk, ak, ak)) + return -1; os_memcpy(kdk, ak, aes_block_size); ak[aes_block_size - 1] ^= 0x01; kdk[aes_block_size - 1] ^= 0x02; - aes_128_encrypt_block(psk, ak, ak); - aes_128_encrypt_block(psk, kdk, kdk); + if (aes_128_encrypt_block(psk, ak, ak) || + aes_128_encrypt_block(psk, kdk, kdk)) + return -1; + return 0; } -void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, - u8 *emsk) +int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) { u8 hash[aes_block_size]; u8 counter = 1; int i; - aes_128_encrypt_block(kdk, rand_p, hash); + if (aes_128_encrypt_block(kdk, rand_p, hash)) + return -1; hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, tek); + if (aes_128_encrypt_block(kdk, hash, tek)) + return -1; hash[aes_block_size - 1] ^= counter; counter++; for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) { hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); + if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size])) + return -1; hash[aes_block_size - 1] ^= counter; counter++; } for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) { hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, &emsk[i * aes_block_size]); + if (aes_128_encrypt_block(kdk, hash, + &emsk[i * aes_block_size])) + return -1; hash[aes_block_size - 1] ^= counter; counter++; } + + return 0; } diff --git a/contrib/hostapd/eap_psk_common.h b/src/eap_common/eap_psk_common.h index e1bdccf5916a4..8adc0541ee03a 100644 --- a/contrib/hostapd/eap_psk_common.h +++ b/src/eap_common/eap_psk_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-PSK shared routines - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -35,21 +35,8 @@ #pragma pack(push, 1) #endif /* _MSC_VER */ -/* 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; -} STRUCT_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 */ @@ -57,10 +44,6 @@ struct eap_psk_hdr_1 { /* 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]; @@ -70,10 +53,6 @@ struct eap_psk_hdr_2 { /* 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]; @@ -82,10 +61,6 @@ struct eap_psk_hdr_3 { /* 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 */ @@ -96,8 +71,8 @@ struct eap_psk_hdr_4 { #endif /* _MSC_VER */ -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, - u8 *emsk); +int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, + u8 *msk, u8 *emsk); #endif /* EAP_PSK_COMMON_H */ diff --git a/contrib/hostapd/eap_sake_common.c b/src/eap_common/eap_sake_common.c index 4b5476f101c80..eafad1d117230 100644 --- a/contrib/hostapd/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,6 +16,7 @@ #include "common.h" #include "sha1.h" +#include "wpabuf.h" #include "eap_defs.h" #include "eap_sake_common.h" @@ -378,3 +379,15 @@ int eap_sake_compute_mic(const u8 *tek_auth, return 0; } + + +void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, + size_t len) +{ + wpabuf_put_u8(buf, type); + wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */ + if (data) + wpabuf_put_data(buf, data, len); + else + os_memset(wpabuf_put(buf, len), 0, len); +} diff --git a/contrib/hostapd/eap_sake_common.h b/src/eap_common/eap_sake_common.h index ac6e8199d66bf..201e20729cb0e 100644 --- a/contrib/hostapd/eap_sake_common.h +++ b/src/eap_common/eap_sake_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -51,10 +51,6 @@ #endif /* _MSC_VER */ struct eap_sake_hdr { - u8 code; - u8 identifier; - u16 length; - u8 type; /* EAP_TYPE_SAKE */ u8 version; /* EAP_SAKE_VERSION */ u8 session_id; u8 subtype; @@ -100,5 +96,7 @@ int eap_sake_compute_mic(const u8 *tek_auth, const u8 *peerid, size_t peerid_len, int peer, const u8 *eap, size_t eap_len, const u8 *mic_pos, u8 *mic); +void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, + size_t len); #endif /* EAP_SAKE_COMMON_H */ diff --git a/contrib/hostapd/eap_sim_common.c b/src/eap_common/eap_sim_common.c index cc430239ab97d..fccda02417aab 100644 --- a/contrib/hostapd/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -1,5 +1,5 @@ /* - * EAP peer: EAP-SIM/AKA shared routines + * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -15,11 +15,13 @@ #include "includes.h" #include "common.h" -#include "eap_i.h" +#include "eap_common/eap_defs.h" #include "sha1.h" +#include "sha256.h" #include "crypto.h" #include "aes_wrap.h" -#include "eap_sim_common.h" +#include "wpabuf.h" +#include "eap_common/eap_sim_common.h" static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen) @@ -165,7 +167,7 @@ int eap_sim_derive_keys_reauth(u16 _counter, } -int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, +int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; @@ -173,23 +175,25 @@ int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, size_t len[2]; u8 *tmp; - if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req || - mac > req + req_len - EAP_SIM_MAC_LEN) + if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || + mac < wpabuf_head_u8(req) || + mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; - tmp = os_malloc(req_len); + tmp = os_malloc(wpabuf_len(req)); if (tmp == NULL) return -1; addr[0] = tmp; - len[0] = req_len; + len[0] = wpabuf_len(req); addr[1] = extra; len[1] = extra_len; /* HMAC-SHA1-128 */ - os_memcpy(tmp, req, req_len); - os_memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", tmp, req_len); + os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); + os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", + tmp, wpabuf_len(req)); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data", extra, extra_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut", @@ -203,7 +207,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, } -void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, +void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; @@ -229,6 +233,272 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, } +#ifdef EAP_AKA_PRIME +static void prf_prime(const u8 *k, const char *seed1, + const u8 *seed2, size_t seed2_len, + const u8 *seed3, size_t seed3_len, + u8 *res, size_t res_len) +{ + const u8 *addr[5]; + size_t len[5]; + u8 hash[SHA256_MAC_LEN]; + u8 iter; + + /* + * PRF'(K,S) = T1 | T2 | T3 | T4 | ... + * T1 = HMAC-SHA-256 (K, S | 0x01) + * T2 = HMAC-SHA-256 (K, T1 | S | 0x02) + * T3 = HMAC-SHA-256 (K, T2 | S | 0x03) + * T4 = HMAC-SHA-256 (K, T3 | S | 0x04) + * ... + */ + + addr[0] = hash; + len[0] = 0; + addr[1] = (const u8 *) seed1; + len[1] = os_strlen(seed1); + addr[2] = seed2; + len[2] = seed2_len; + addr[3] = seed3; + len[3] = seed3_len; + addr[4] = &iter; + len[4] = 1; + + iter = 0; + while (res_len) { + size_t hlen; + iter++; + hmac_sha256_vector(k, 32, 5, addr, len, hash); + len[0] = SHA256_MAC_LEN; + hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len; + os_memcpy(res, hash, hlen); + res += hlen; + res_len -= hlen; + } +} + + +void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *k_encr, + u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk) +{ + u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN]; + u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN + + EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN]; + u8 *pos; + + /* + * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity) + * K_encr = MK[0..127] + * K_aut = MK[128..383] + * K_re = MK[384..639] + * MSK = MK[640..1151] + * EMSK = MK[1152..1663] + */ + + os_memcpy(key, ik, EAP_AKA_IK_LEN); + os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN); + + prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0, + keys, sizeof(keys)); + + pos = keys; + os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr", + k_encr, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + + os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + pos += EAP_AKA_PRIME_K_AUT_LEN; + + os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re", + k_re, EAP_AKA_PRIME_K_RE_LEN); + pos += EAP_AKA_PRIME_K_RE_LEN; + + os_memcpy(msk, pos, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + os_memcpy(emsk, pos, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); +} + + +int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, u8 *msk, u8 *emsk) +{ + u8 seed3[2 + EAP_SIM_NONCE_S_LEN]; + u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN]; + u8 *pos; + + /* + * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S) + * MSK = MK[0..511] + * EMSK = MK[512..1023] + */ + + WPA_PUT_BE16(seed3, counter); + os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN); + + prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len, + seed3, sizeof(seed3), + keys, sizeof(keys)); + + pos = keys; + os_memcpy(msk, pos, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + os_memcpy(emsk, pos, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); + + os_memset(keys, 0, sizeof(keys)); + + return 0; +} + + +int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 *tmp; + + if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || + mac < wpabuf_head_u8(req) || + mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) + return -1; + + tmp = os_malloc(wpabuf_len(req)); + if (tmp == NULL) + return -1; + + addr[0] = tmp; + len[0] = wpabuf_len(req); + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA-256-128 */ + os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); + os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", + tmp, wpabuf_len(req)); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC", + hmac, EAP_SIM_MAC_LEN); + os_free(tmp); + + return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; +} + + +void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, + u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = msg; + len[0] = msg_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA-256-128 */ + os_memset(mac, 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); + os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC", + mac, EAP_SIM_MAC_LEN); +} + + +void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, + const u8 *network_name, + size_t network_name_len) +{ + u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN]; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[5]; + size_t len[5]; + u8 fc; + u8 l0[2], l1[2]; + + /* 3GPP TS 33.402 V8.0.0 + * (CK', IK') = F(CK, IK, <access network identity>) + */ + /* TODO: CK', IK' generation should really be moved into the actual + * AKA procedure with network name passed in there and option to use + * AMF separation bit = 1 (3GPP TS 33.401). */ + + /* Change Request 33.402 CR 0033 to version 8.1.1 from + * 3GPP TSG-SA WG3 Meeting #53 in September 2008: + * + * CK' || IK' = HMAC-SHA-256(Key, S) + * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln + * Key = CK || IK + * FC = 0x20 + * P0 = access network identity (3GPP TS 24.302) + * L0 = length of acceess network identity (2 octets, big endian) + * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0 + * L1 = 0x00 0x06 + */ + + fc = 0x20; + + wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)"); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN); + wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity", + network_name, network_name_len); + wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6); + + os_memcpy(key, ck, EAP_AKA_CK_LEN); + os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK", + key, sizeof(key)); + + addr[0] = &fc; + len[0] = 1; + addr[1] = network_name; + len[1] = network_name_len; + WPA_PUT_BE16(l0, network_name_len); + addr[2] = l0; + len[2] = 2; + addr[3] = sqn_ak; + len[3] = 6; + WPA_PUT_BE16(l1, 6); + addr[4] = l1; + len[4] = 2; + + hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')", + hash, sizeof(hash)); + + os_memcpy(ck, hash, EAP_AKA_CK_LEN); + os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN); +} +#endif /* EAP_AKA_PRIME */ + + int eap_sim_parse_attr(const u8 *start, const u8 *end, struct eap_sim_attrs *attr, int aka, int encr) { @@ -354,8 +624,20 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, break; case EAP_SIM_AT_IDENTITY: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); - attr->identity = apos + 2; - attr->identity_len = alen - 2; + plen = WPA_GET_BE16(apos); + apos += 2; + alen -= 2; + if (plen > alen) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_IDENTITY (Actual Length %lu, " + "remaining length %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + + attr->identity = apos; + attr->identity_len = plen; break; case EAP_SIM_AT_VERSION_LIST: if (aka) { @@ -514,6 +796,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, break; case EAP_SIM_AT_RES: wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES"); + attr->res_len_bits = WPA_GET_BE16(apos); apos += 2; alen -= 2; if (!aka || alen < EAP_AKA_MIN_RES_LEN || @@ -541,6 +824,96 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, } attr->auts = apos; break; + case EAP_SIM_AT_CHECKCODE: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_CHECKCODE"); + return -1; + } + apos += 2; + alen -= 2; + if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN && + alen != EAP_AKA_PRIME_CHECKCODE_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid " + "AT_CHECKCODE (len %lu)", + (unsigned long) alen); + return -1; + } + attr->checkcode = apos; + attr->checkcode_len = alen; + break; + case EAP_SIM_AT_RESULT_IND: + if (encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted " + "AT_RESULT_IND"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_RESULT_IND (alen=%lu)", + (unsigned long) alen); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); + attr->result_ind = 1; + break; +#ifdef EAP_AKA_PRIME + case EAP_SIM_AT_KDF_INPUT: + if (aka != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " + "AT_KDF_INPUT"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT"); + plen = WPA_GET_BE16(apos); + apos += 2; + alen -= 2; + if (plen > alen) { + wpa_printf(MSG_INFO, "EAP-AKA': Invalid " + "AT_KDF_INPUT (Actual Length %lu, " + "remaining length %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->kdf_input = apos; + attr->kdf_input_len = plen; + break; + case EAP_SIM_AT_KDF: + if (aka != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " + "AT_KDF"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-AKA': Invalid " + "AT_KDF (len %lu)", + (unsigned long) alen); + return -1; + } + if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) { + wpa_printf(MSG_DEBUG, "EAP-AKA': Too many " + "AT_KDF attributes - ignore this"); + continue; + } + attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos); + attr->kdf_count++; + break; + case EAP_SIM_AT_BIDDING: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid " + "AT_BIDDING (len %lu)", + (unsigned long) alen); + return -1; + } + attr->bidding = apos; + break; +#endif /* EAP_AKA_PRIME */ default: if (pos[0] < 128) { wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " @@ -580,7 +953,10 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, return NULL; os_memcpy(decrypted, encr_data, encr_data_len); - aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); + if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { + os_free(decrypted); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", decrypted, encr_data_len); @@ -599,9 +975,9 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, #define EAP_SIM_INIT_LEN 128 struct eap_sim_msg { - u8 *buf; - size_t buf_len, used; + struct wpabuf *buf; size_t mac, iv, encr; /* index from buf */ + int type; }; @@ -615,46 +991,53 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) if (msg == NULL) return NULL; - msg->buf = os_zalloc(EAP_SIM_INIT_LEN); + msg->type = type; + msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN); if (msg->buf == NULL) { os_free(msg); return NULL; } - msg->buf_len = EAP_SIM_INIT_LEN; - eap = (struct eap_hdr *) msg->buf; + eap = wpabuf_put(msg->buf, sizeof(*eap)); eap->code = code; eap->identifier = id; - msg->used = sizeof(*eap); - pos = (u8 *) (eap + 1); + pos = wpabuf_put(msg->buf, 4); *pos++ = type; *pos++ = subtype; *pos++ = 0; /* Reserved */ *pos++ = 0; /* Reserved */ - msg->used += 4; return msg; } -u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, - const u8 *extra, size_t extra_len) +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, + const u8 *extra, size_t extra_len) { struct eap_hdr *eap; - u8 *buf; + struct wpabuf *buf; if (msg == NULL) return NULL; - eap = (struct eap_hdr *) msg->buf; - eap->length = host_to_be16(msg->used); - + eap = wpabuf_mhead(msg->buf); + eap->length = host_to_be16(wpabuf_len(msg->buf)); + +#ifdef EAP_AKA_PRIME + if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { + eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), + wpabuf_len(msg->buf), + (u8 *) wpabuf_mhead(msg->buf) + + msg->mac, extra, extra_len); + } else +#endif /* EAP_AKA_PRIME */ if (k_aut && msg->mac) { - eap_sim_add_mac(k_aut, msg->buf, msg->used, - msg->buf + msg->mac, extra, extra_len); + eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf), + wpabuf_len(msg->buf), + (u8 *) wpabuf_mhead(msg->buf) + msg->mac, + extra, extra_len); } - *len = msg->used; buf = msg->buf; os_free(msg); return buf; @@ -664,48 +1047,32 @@ u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, void eap_sim_msg_free(struct eap_sim_msg *msg) { if (msg) { - os_free(msg->buf); + wpabuf_free(msg->buf); os_free(msg); } } -static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len) -{ - if (msg->used + add_len > msg->buf_len) { - u8 *nbuf = os_realloc(msg->buf, msg->used + add_len); - if (nbuf == NULL) - return -1; - msg->buf = nbuf; - msg->buf_len = msg->used + add_len; - } - return 0; -} - - u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, const u8 *data, size_t len) { int attr_len = 2 + len; int pad_len; - u8 *start, *pos; + u8 *start; if (msg == NULL) return NULL; pad_len = (4 - attr_len % 4) % 4; attr_len += pad_len; - if (eap_sim_msg_resize(msg, attr_len)) + if (wpabuf_resize(&msg->buf, attr_len)) return NULL; - start = pos = msg->buf + msg->used; - *pos++ = attr; - *pos++ = attr_len / 4; - os_memcpy(pos, data, len); - if (pad_len) { - pos += len; - os_memset(pos, 0, pad_len); - } - msg->used += attr_len; + start = wpabuf_put(msg->buf, 0); + wpabuf_put_u8(msg->buf, attr); + wpabuf_put_u8(msg->buf, attr_len / 4); + wpabuf_put_data(msg->buf, data, len); + if (pad_len) + os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); return start; } @@ -715,27 +1082,25 @@ u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, { int attr_len = 4 + len; int pad_len; - u8 *start, *pos; + u8 *start; if (msg == NULL) return NULL; pad_len = (4 - attr_len % 4) % 4; attr_len += pad_len; - if (eap_sim_msg_resize(msg, attr_len)) + if (wpabuf_resize(&msg->buf, attr_len)) return NULL; - start = pos = msg->buf + msg->used; - *pos++ = attr; - *pos++ = attr_len / 4; - WPA_PUT_BE16(pos, value); - pos += 2; + start = wpabuf_put(msg->buf, 0); + wpabuf_put_u8(msg->buf, attr); + wpabuf_put_u8(msg->buf, attr_len / 4); + wpabuf_put_be16(msg->buf, value); if (data) - os_memcpy(pos, data, len); - if (pad_len) { - pos += len; - os_memset(pos, 0, pad_len); - } - msg->used += attr_len; + wpabuf_put_data(msg->buf, data, len); + else + wpabuf_put(msg->buf, len); + if (pad_len) + os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); return start; } @@ -744,7 +1109,7 @@ u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) { u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); if (pos) - msg->mac = (pos - msg->buf) + 4; + msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4; return pos; } @@ -755,8 +1120,9 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); if (pos == NULL) return -1; - msg->iv = (pos - msg->buf) + 4; - if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) { + msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; + if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv, + EAP_SIM_IV_LEN)) { msg->iv = 0; return -1; } @@ -766,7 +1132,7 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, msg->iv = 0; return -1; } - msg->encr = pos - msg->buf; + msg->encr = pos - wpabuf_head_u8(msg->buf); return 0; } @@ -779,7 +1145,7 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0) return -1; - encr_len = msg->used - msg->encr - 4; + encr_len = wpabuf_len(msg->buf) - msg->encr - 4; if (encr_len % 16) { u8 *pos; int pad_len = 16 - (encr_len % 16); @@ -798,11 +1164,10 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) } wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", (unsigned long) encr_len); - msg->buf[msg->encr + 1] = encr_len / 4 + 1; - aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv, - msg->buf + msg->encr + 4, encr_len); - - return 0; + wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1; + return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv, + wpabuf_mhead_u8(msg->buf) + msg->encr + 4, + encr_len); } diff --git a/contrib/hostapd/eap_sim_common.h b/src/eap_common/eap_sim_common.h index 9c983a864e3e2..a8080e27a7b3b 100644 --- a/contrib/hostapd/eap_sim_common.h +++ b/src/eap_common/eap_sim_common.h @@ -1,6 +1,6 @@ /* - * EAP peer: EAP-SIM/AKA shared routines - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * EAP peer/server: EAP-SIM/AKA/AKA' shared routines + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -69,6 +69,13 @@ #define EAP_AKA_MAX_FAST_REAUTHS 1000 #define EAP_AKA_MIN_RES_LEN 4 #define EAP_AKA_MAX_RES_LEN 16 +#define EAP_AKA_CHECKCODE_LEN 20 + +#define EAP_AKA_PRIME_K_AUT_LEN 32 +#define EAP_AKA_PRIME_CHECKCODE_LEN 32 +#define EAP_AKA_PRIME_K_RE_LEN 32 + +struct wpabuf; void eap_sim_derive_mk(const u8 *identity, size_t identity_len, const u8 *nonce_mt, u16 selected_version, @@ -82,11 +89,54 @@ int eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk, u8 *emsk); -int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, +int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, const u8 *mac, const u8 *extra, size_t extra_len); -void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, +void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); +#ifdef EAP_AKA_PRIME +void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *k_encr, + u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk); +int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, u8 *msk, u8 *emsk); +int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len); +void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, + u8 *mac, const u8 *extra, size_t extra_len); + +void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, + const u8 *network_name, + size_t network_name_len); +#else /* EAP_AKA_PRIME */ +static inline void eap_aka_prime_derive_keys(const u8 *identity, + size_t identity_len, + const u8 *ik, const u8 *ck, + u8 *k_encr, u8 *k_aut, u8 *k_re, + u8 *msk, u8 *emsk) +{ +} + +static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, + size_t identity_len, + const u8 *nonce_s, u8 *msk, + u8 *emsk) +{ + return -1; +} + +static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + return -1; +} +#endif /* EAP_AKA_PRIME */ + /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ #define EAP_SIM_AT_RAND 1 @@ -107,12 +157,15 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, #define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ #define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ #define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ +#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */ +#define EAP_SIM_AT_KDF 24 /* only AKA' */ #define EAP_SIM_AT_IV 129 #define EAP_SIM_AT_ENCR_DATA 130 #define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ #define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ #define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ #define EAP_SIM_AT_RESULT_IND 135 +#define EAP_SIM_AT_BIDDING 136 /* AT_NOTIFICATION notification code values */ #define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 @@ -121,6 +174,12 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, #define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 #define EAP_SIM_SUCCESS 32768 +/* EAP-AKA' AT_KDF Key Derivation Function values */ +#define EAP_AKA_PRIME_KDF 1 + +/* AT_BIDDING flags */ +#define EAP_AKA_BIDDING_FLAG_D 0x8000 + enum eap_sim_id_req { NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID @@ -131,11 +190,21 @@ struct eap_sim_attrs { const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; const u8 *next_pseudonym, *next_reauth_id; const u8 *nonce_mt, *identity, *res, *auts; + const u8 *checkcode; + const u8 *kdf_input; + const u8 *bidding; size_t num_chal, version_list_len, encr_data_len; size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len; + size_t res_len_bits; + size_t checkcode_len; + size_t kdf_input_len; enum eap_sim_id_req id_req; int notification, counter, selected_version, client_error_code; int counter_too_small; + int result_ind; +#define EAP_AKA_PRIME_KDF_MAX 10 + u16 kdf[EAP_AKA_PRIME_KDF_MAX]; + size_t kdf_count; }; int eap_sim_parse_attr(const u8 *start, const u8 *end, @@ -148,8 +217,8 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, struct eap_sim_msg; struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); -u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, - const u8 *extra, size_t extra_len); +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, + const u8 *extra, size_t extra_len); void eap_sim_msg_free(struct eap_sim_msg *msg); u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, const u8 *data, size_t len); diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h new file mode 100644 index 0000000000000..e3836d5c140a1 --- /dev/null +++ b/src/eap_common/eap_tlv_common.h @@ -0,0 +1,119 @@ +/* + * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_TLV_COMMON_H +#define EAP_TLV_COMMON_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_ERROR_CODE_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* draft-cam-winget-eap-fast-provisioning-04.txt, + * Section 4.2 */ +#define EAP_TLV_CRYPTO_BINDING_TLV 12 +#define EAP_TLV_CALLING_STATION_ID_TLV 13 +#define EAP_TLV_CALLED_STATION_ID_TLV 14 +#define EAP_TLV_NAS_PORT_TYPE_TLV 15 +#define EAP_TLV_SERVER_IDENTIFIER_TLV 16 +#define EAP_TLV_IDENTITY_TYPE_TLV 17 +#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18 +#define EAP_TLV_REQUEST_ACTION_TLV 19 +#define EAP_TLV_PKCS7_TLV 20 + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 +#define EAP_TLV_TYPE_MASK 0x3fff + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_tlv_hdr { + be16 tlv_type; + be16 length; +} STRUCT_PACKED; + +struct eap_tlv_nak_tlv { + be16 tlv_type; + be16 length; + be32 vendor_id; + be16 nak_type; +} STRUCT_PACKED; + +struct eap_tlv_result_tlv { + be16 tlv_type; + be16 length; + be16 status; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */ +struct eap_tlv_intermediate_result_tlv { + be16 tlv_type; + be16 length; + be16 status; + /* Followed by optional TLVs */ +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */ +struct eap_tlv_crypto_binding_tlv { + be16 tlv_type; + be16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} STRUCT_PACKED; + +struct eap_tlv_pac_ack_tlv { + be16 tlv_type; + be16 length; + be16 pac_type; + be16 pac_len; + be16 result; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.9 - Request-Action TLV */ +struct eap_tlv_request_action_tlv { + be16 tlv_type; + be16 length; + be16 action; +} STRUCT_PACKED; + +/* draft-cam-winget-eap-fast-provisiong-04.txt, Section 4.2.6 - PAC-Type TLV */ +struct eap_tlv_pac_type_tlv { + be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ + be16 length; + be16 pac_type; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + +#define EAP_TLV_ACTION_PROCESS_TLV 1 +#define EAP_TLV_ACTION_NEGOTIATE_EAP 2 + +#endif /* EAP_TLV_COMMON_H */ diff --git a/contrib/hostapd/eap_ttls.h b/src/eap_common/eap_ttls.h index e0b2cbf45488e..797d084786f3d 100644 --- a/contrib/hostapd/eap_ttls.h +++ b/src/eap_common/eap_ttls.h @@ -1,6 +1,6 @@ /* - * EAP server/peer: EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * EAP server/peer: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,18 +16,18 @@ #define EAP_TTLS_H struct ttls_avp { - u32 avp_code; - u32 avp_length; /* 8-bit flags, 24-bit length; - * length includes AVP header */ + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ /* optional 32-bit Vendor-ID */ /* Data */ }; struct ttls_avp_vendor { - u32 avp_code; - u32 avp_length; /* 8-bit flags, 24-bit length; - * length includes AVP header */ - u32 vendor_id; + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + be32 vendor_id; /* Data */ }; diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c new file mode 100644 index 0000000000000..5d4e8cc1140cf --- /dev/null +++ b/src/eap_common/eap_wsc_common.c @@ -0,0 +1,39 @@ +/* + * EAP-WSC common routines for Wi-Fi Protected Setup + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_defs.h" +#include "eap_common.h" +#include "wps/wps.h" +#include "eap_wsc_common.h" + +struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "FRAG_ACK"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK"); + wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */ + wpabuf_put_u8(msg, 0); /* Flags */ + + return msg; +} diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h new file mode 100644 index 0000000000000..fdf61d317de60 --- /dev/null +++ b/src/eap_common/eap_wsc_common.h @@ -0,0 +1,33 @@ +/* + * EAP-WSC definitions for Wi-Fi Protected Setup + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_WSC_COMMON_H +#define EAP_WSC_COMMON_H + +#define EAP_VENDOR_TYPE_WSC 1 + +#define WSC_FLAGS_MF 0x01 +#define WSC_FLAGS_LF 0x02 + +#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0" +#define WSC_ID_REGISTRAR_LEN 30 +#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0" +#define WSC_ID_ENROLLEE_LEN 29 + +#define WSC_FRAGMENT_SIZE 1400 + + +struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code); + +#endif /* EAP_WSC_COMMON_H */ diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c new file mode 100644 index 0000000000000..818b5bdbaad8e --- /dev/null +++ b/src/eap_common/ikev2_common.c @@ -0,0 +1,796 @@ +/* + * IKEv2 common routines for initiator and responder + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" +#include "ikev2_common.h" + + +static struct ikev2_integ_alg ikev2_integ_algs[] = { + { AUTH_HMAC_SHA1_96, 20, 12 }, + { AUTH_HMAC_MD5_96, 16, 12 } +}; + +#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0])) + + +static struct ikev2_prf_alg ikev2_prf_algs[] = { + { PRF_HMAC_SHA1, 20, 20 }, + { PRF_HMAC_MD5, 16, 16 } +}; + +#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0])) + + +static struct ikev2_encr_alg ikev2_encr_algs[] = { + { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ + { ENCR_3DES, 24, 8 } +}; + +#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0])) + + +const struct ikev2_integ_alg * ikev2_get_integ(int id) +{ + size_t i; + + for (i = 0; i < NUM_INTEG_ALGS; i++) { + if (ikev2_integ_algs[i].id == id) + return &ikev2_integ_algs[i]; + } + + return NULL; +} + + +int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *hash) +{ + u8 tmphash[IKEV2_MAX_HASH_LEN]; + + switch (alg) { + case AUTH_HMAC_SHA1_96: + if (key_len != 20) + return -1; + hmac_sha1(key, key_len, data, data_len, tmphash); + os_memcpy(hash, tmphash, 12); + break; + case AUTH_HMAC_MD5_96: + if (key_len != 16) + return -1; + hmac_md5(key, key_len, data, data_len, tmphash); + os_memcpy(hash, tmphash, 12); + break; + default: + return -1; + } + + return 0; +} + + +const struct ikev2_prf_alg * ikev2_get_prf(int id) +{ + size_t i; + + for (i = 0; i < NUM_PRF_ALGS; i++) { + if (ikev2_prf_algs[i].id == id) + return &ikev2_prf_algs[i]; + } + + return NULL; +} + + +int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *hash) +{ + switch (alg) { + case PRF_HMAC_SHA1: + hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); + break; + case PRF_HMAC_MD5: + hmac_md5_vector(key, key_len, num_elem, addr, len, hash); + break; + default: + return -1; + } + + return 0; +} + + +int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, + u8 *out, size_t out_len) +{ + u8 hash[IKEV2_MAX_HASH_LEN]; + size_t hash_len; + u8 iter, *pos, *end; + const u8 *addr[3]; + size_t len[3]; + const struct ikev2_prf_alg *prf; + int res; + + prf = ikev2_get_prf(alg); + if (prf == NULL) + return -1; + hash_len = prf->hash_len; + + addr[0] = hash; + len[0] = hash_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &iter; + len[2] = 1; + + pos = out; + end = out + out_len; + iter = 1; + while (pos < end) { + size_t clen; + if (iter == 1) + res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1], + &len[1], hash); + else + res = ikev2_prf_hash(alg, key, key_len, 3, addr, len, + hash); + if (res < 0) + return -1; + clen = hash_len; + if ((int) clen > end - pos) + clen = end - pos; + os_memcpy(pos, hash, clen); + pos += clen; + iter++; + } + + return 0; +} + + +const struct ikev2_encr_alg * ikev2_get_encr(int id) +{ + size_t i; + + for (i = 0; i < NUM_ENCR_ALGS; i++) { + if (ikev2_encr_algs[i].id == id) + return &ikev2_encr_algs[i]; + } + + return NULL; +} + + +#ifdef CCNS_PL +/* from des.c */ +struct des3_key_s { + u32 ek[3][32]; + u32 dk[3][32]; +}; + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey); +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); +#endif /* CCNS_PL */ + + +int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *plain, u8 *crypt, size_t len) +{ + struct crypto_cipher *cipher; + int encr_alg; + +#ifdef CCNS_PL + if (alg == ENCR_3DES) { + struct des3_key_s des3key; + size_t i, blocks; + u8 *pos; + + /* ECB mode is used incorrectly for 3DES!? */ + if (key_len != 24) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); + return -1; + } + des3_key_setup(key, &des3key); + + blocks = len / 8; + pos = crypt; + for (i = 0; i < blocks; i++) { + des3_encrypt(pos, &des3key, pos); + pos += 8; + } + } else { +#endif /* CCNS_PL */ + switch (alg) { + case ENCR_3DES: + encr_alg = CRYPTO_CIPHER_ALG_3DES; + break; + case ENCR_AES_CBC: + encr_alg = CRYPTO_CIPHER_ALG_AES; + break; + default: + wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); + return -1; + } + + cipher = crypto_cipher_init(encr_alg, iv, key, key_len); + if (cipher == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); + return -1; + } + + if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Encryption failed"); + crypto_cipher_deinit(cipher); + return -1; + } + crypto_cipher_deinit(cipher); +#ifdef CCNS_PL + } +#endif /* CCNS_PL */ + + return 0; +} + + +int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *crypt, u8 *plain, size_t len) +{ + struct crypto_cipher *cipher; + int encr_alg; + +#ifdef CCNS_PL + if (alg == ENCR_3DES) { + struct des3_key_s des3key; + size_t i, blocks; + + /* ECB mode is used incorrectly for 3DES!? */ + if (key_len != 24) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); + return -1; + } + des3_key_setup(key, &des3key); + + if (len % 8) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted " + "length"); + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + des3_decrypt(crypt, &des3key, plain); + plain += 8; + crypt += 8; + } + } else { +#endif /* CCNS_PL */ + switch (alg) { + case ENCR_3DES: + encr_alg = CRYPTO_CIPHER_ALG_3DES; + break; + case ENCR_AES_CBC: + encr_alg = CRYPTO_CIPHER_ALG_AES; + break; + default: + wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); + return -1; + } + + cipher = crypto_cipher_init(encr_alg, iv, key, key_len); + if (cipher == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); + return -1; + } + + if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Decryption failed"); + crypto_cipher_deinit(cipher); + return -1; + } + crypto_cipher_deinit(cipher); +#ifdef CCNS_PL + } +#endif /* CCNS_PL */ + + return 0; +} + + +int ikev2_parse_payloads(struct ikev2_payloads *payloads, + u8 next_payload, const u8 *pos, const u8 *end) +{ + const struct ikev2_payload_hdr *phdr; + + os_memset(payloads, 0, sizeof(*payloads)); + + while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) { + int plen, pdatalen; + const u8 *pdata; + wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u", + next_payload); + if (end - pos < (int) sizeof(*phdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short message for " + "payload header (left=%ld)", + (long) (end - pos)); + } + phdr = (const struct ikev2_payload_hdr *) pos; + plen = WPA_GET_BE16(phdr->payload_length); + if (plen < (int) sizeof(*phdr) || pos + plen > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid payload header " + "length %d", plen); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x" + " Payload Length: %d", + phdr->next_payload, phdr->flags, plen); + + pdata = (const u8 *) (phdr + 1); + pdatalen = plen - sizeof(*phdr); + + switch (next_payload) { + case IKEV2_PAYLOAD_SA: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Security " + "Association"); + payloads->sa = pdata; + payloads->sa_len = pdatalen; + break; + case IKEV2_PAYLOAD_KEY_EXCHANGE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Key " + "Exchange"); + payloads->ke = pdata; + payloads->ke_len = pdatalen; + break; + case IKEV2_PAYLOAD_IDi: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDi"); + payloads->idi = pdata; + payloads->idi_len = pdatalen; + break; + case IKEV2_PAYLOAD_IDr: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDr"); + payloads->idr = pdata; + payloads->idr_len = pdatalen; + break; + case IKEV2_PAYLOAD_CERTIFICATE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Certificate"); + payloads->cert = pdata; + payloads->cert_len = pdatalen; + break; + case IKEV2_PAYLOAD_AUTHENTICATION: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: " + "Authentication"); + payloads->auth = pdata; + payloads->auth_len = pdatalen; + break; + case IKEV2_PAYLOAD_NONCE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Nonce"); + payloads->nonce = pdata; + payloads->nonce_len = pdatalen; + break; + case IKEV2_PAYLOAD_ENCRYPTED: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Encrypted"); + payloads->encrypted = pdata; + payloads->encrypted_len = pdatalen; + break; + case IKEV2_PAYLOAD_NOTIFICATION: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: " + "Notification"); + payloads->notification = pdata; + payloads->notification_len = pdatalen; + break; + default: + if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported " + "critical payload %u - reject the " + "entire message", next_payload); + return -1; + } else { + wpa_printf(MSG_DEBUG, "IKEV2: Skipped " + "unsupported payload %u", + next_payload); + } + } + + if (next_payload == IKEV2_PAYLOAD_ENCRYPTED && + pos + plen == end) { + /* + * Next Payload in the case of Encrypted Payload is + * actually the payload type for the first embedded + * payload. + */ + payloads->encr_next_payload = phdr->next_payload; + next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD; + } else + next_payload = phdr->next_payload; + + pos += plen; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after " + "payloads"); + return -1; + } + + return 0; +} + + +int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, + const u8 *ID, size_t ID_len, u8 ID_type, + struct ikev2_keys *keys, int initiator, + const u8 *shared_secret, size_t shared_secret_len, + const u8 *nonce, size_t nonce_len, + const u8 *key_pad, size_t key_pad_len, + u8 *auth_data) +{ + size_t sign_len, buf_len; + u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr; + + prf = ikev2_get_prf(prf_alg); + if (sign_msg == NULL || ID == NULL || SK_p == NULL || + shared_secret == NULL || nonce == NULL || prf == NULL) + return -1; + + /* prf(SK_pi/r,IDi/r') */ + buf_len = 4 + ID_len; + buf = os_zalloc(buf_len); + if (buf == NULL) + return -1; + buf[0] = ID_type; + os_memcpy(buf + 4, ID, ID_len); + if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len, + 1, (const u8 **) &buf, &buf_len, hash) < 0) { + os_free(buf); + return -1; + } + os_free(buf); + + /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */ + sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len; + sign_data = os_malloc(sign_len); + if (sign_data == NULL) + return -1; + pos = sign_data; + os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg)); + pos += wpabuf_len(sign_msg); + os_memcpy(pos, nonce, nonce_len); + pos += nonce_len; + os_memcpy(pos, hash, prf->hash_len); + + /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */ + if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1, + &key_pad, &key_pad_len, hash) < 0 || + ikev2_prf_hash(prf->id, hash, prf->hash_len, 1, + (const u8 **) &sign_data, &sign_len, auth_data) < 0) + { + os_free(sign_data); + return -1; + } + os_free(sign_data); + + return 0; +} + + +u8 * ikev2_decrypt_payload(int encr_id, int integ_id, + struct ikev2_keys *keys, int initiator, + const struct ikev2_hdr *hdr, + const u8 *encrypted, size_t encrypted_len, + size_t *res_len) +{ + size_t iv_len; + const u8 *pos, *end, *iv, *integ; + u8 hash[IKEV2_MAX_HASH_LEN], *decrypted; + size_t decrypted_len, pad_len; + const struct ikev2_integ_alg *integ_alg; + const struct ikev2_encr_alg *encr_alg; + const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + if (encrypted == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH"); + return NULL; + } + + encr_alg = ikev2_get_encr(encr_id); + if (encr_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); + return NULL; + } + iv_len = encr_alg->block_size; + + integ_alg = ikev2_get_integ(integ_id); + if (integ_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); + return NULL; + } + + if (encrypted_len < iv_len + 1 + integ_alg->hash_len) { + wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity " + "Checksum"); + return NULL; + } + + iv = encrypted; + pos = iv + iv_len; + end = encrypted + encrypted_len; + integ = end - integ_alg->hash_len; + + if (SK_a == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); + return NULL; + } + if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, + (const u8 *) hdr, + integ - (const u8 *) hdr, hash) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity " + "hash"); + return NULL; + } + if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum " + "Data"); + return NULL; + } + + if (SK_e == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); + return NULL; + } + + decrypted_len = integ - pos; + decrypted = os_malloc(decrypted_len); + if (decrypted == NULL) + return NULL; + + if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos, + decrypted, decrypted_len) < 0) { + os_free(decrypted); + return NULL; + } + + pad_len = decrypted[decrypted_len - 1]; + if (decrypted_len < pad_len + 1) { + wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted " + "payload"); + os_free(decrypted); + return NULL; + } + + decrypted_len -= pad_len + 1; + + *res_len = decrypted_len; + return decrypted; +} + + +void ikev2_update_hdr(struct wpabuf *msg) +{ + struct ikev2_hdr *hdr; + + /* Update lenth field in HDR */ + hdr = wpabuf_mhead(msg); + WPA_PUT_BE32(hdr->length, wpabuf_len(msg)); +} + + +int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, struct wpabuf *msg, + struct wpabuf *plain, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + size_t iv_len, pad_len; + u8 *icv, *iv; + const struct ikev2_integ_alg *integ_alg; + const struct ikev2_encr_alg *encr_alg; + const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload"); + + /* Encr - RFC 4306, Sect. 3.14 */ + + encr_alg = ikev2_get_encr(encr_id); + if (encr_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); + return -1; + } + iv_len = encr_alg->block_size; + + integ_alg = ikev2_get_integ(integ_id); + if (integ_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); + return -1; + } + + if (SK_e == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); + return -1; + } + + if (SK_a == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); + return -1; + } + + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + iv = wpabuf_put(msg, iv_len); + if (os_get_random(iv, iv_len)) { + wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); + return -1; + } + + pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; + if (pad_len == iv_len) + pad_len = 0; + wpabuf_put(plain, pad_len); + wpabuf_put_u8(plain, pad_len); + + if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, + wpabuf_head(plain), wpabuf_mhead(plain), + wpabuf_len(plain)) < 0) + return -1; + + wpabuf_put_buf(msg, plain); + + /* Need to update all headers (Length fields) prior to hash func */ + icv = wpabuf_put(msg, integ_alg->hash_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + ikev2_update_hdr(msg); + + return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, + wpabuf_head(msg), + wpabuf_len(msg) - integ_alg->hash_len, icv); + + return 0; +} + + +int ikev2_keys_set(struct ikev2_keys *keys) +{ + return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei && + keys->SK_er && keys->SK_pi && keys->SK_pr; +} + + +void ikev2_free_keys(struct ikev2_keys *keys) +{ + os_free(keys->SK_d); + os_free(keys->SK_ai); + os_free(keys->SK_ar); + os_free(keys->SK_ei); + os_free(keys->SK_er); + os_free(keys->SK_pi); + os_free(keys->SK_pr); + keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er = + keys->SK_pi = keys->SK_pr = NULL; +} + + +int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, + const struct ikev2_integ_alg *integ, + const struct ikev2_encr_alg *encr, + const u8 *skeyseed, const u8 *data, size_t data_len, + struct ikev2_keys *keys) +{ + u8 *keybuf, *pos; + size_t keybuf_len; + + /* + * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = + * prf+(SKEYSEED, Ni | Nr | SPIi | SPIr ) + */ + ikev2_free_keys(keys); + keys->SK_d_len = prf->key_len; + keys->SK_integ_len = integ->key_len; + keys->SK_encr_len = encr->key_len; + keys->SK_prf_len = prf->key_len; +#ifdef CCNS_PL + /* Uses encryption key length for SK_d; should be PRF length */ + keys->SK_d_len = keys->SK_encr_len; +#endif /* CCNS_PL */ + + keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len + + 2 * keys->SK_encr_len + 2 * keys->SK_prf_len; + keybuf = os_malloc(keybuf_len); + if (keybuf == NULL) + return -1; + + if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len, + data, data_len, keybuf, keybuf_len)) { + os_free(keybuf); + return -1; + } + + pos = keybuf; + + keys->SK_d = os_malloc(keys->SK_d_len); + if (keys->SK_d) { + os_memcpy(keys->SK_d, pos, keys->SK_d_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d", + keys->SK_d, keys->SK_d_len); + } + pos += keys->SK_d_len; + + keys->SK_ai = os_malloc(keys->SK_integ_len); + if (keys->SK_ai) { + os_memcpy(keys->SK_ai, pos, keys->SK_integ_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai", + keys->SK_ai, keys->SK_integ_len); + } + pos += keys->SK_integ_len; + + keys->SK_ar = os_malloc(keys->SK_integ_len); + if (keys->SK_ar) { + os_memcpy(keys->SK_ar, pos, keys->SK_integ_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar", + keys->SK_ar, keys->SK_integ_len); + } + pos += keys->SK_integ_len; + + keys->SK_ei = os_malloc(keys->SK_encr_len); + if (keys->SK_ei) { + os_memcpy(keys->SK_ei, pos, keys->SK_encr_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei", + keys->SK_ei, keys->SK_encr_len); + } + pos += keys->SK_encr_len; + + keys->SK_er = os_malloc(keys->SK_encr_len); + if (keys->SK_er) { + os_memcpy(keys->SK_er, pos, keys->SK_encr_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er", + keys->SK_er, keys->SK_encr_len); + } + pos += keys->SK_encr_len; + + keys->SK_pi = os_malloc(keys->SK_prf_len); + if (keys->SK_pi) { + os_memcpy(keys->SK_pi, pos, keys->SK_prf_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi", + keys->SK_pi, keys->SK_prf_len); + } + pos += keys->SK_prf_len; + + keys->SK_pr = os_malloc(keys->SK_prf_len); + if (keys->SK_pr) { + os_memcpy(keys->SK_pr, pos, keys->SK_prf_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr", + keys->SK_pr, keys->SK_prf_len); + } + + os_free(keybuf); + + if (!ikev2_keys_set(keys)) { + ikev2_free_keys(keys); + return -1; + } + + return 0; +} diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h new file mode 100644 index 0000000000000..c96a070d5bcb8 --- /dev/null +++ b/src/eap_common/ikev2_common.h @@ -0,0 +1,344 @@ +/* + * IKEv2 definitions + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IKEV2_COMMON_H +#define IKEV2_COMMON_H + +/* + * Nonce length must be at least 16 octets. It must also be at least half the + * key size of the negotiated PRF. + */ +#define IKEV2_NONCE_MIN_LEN 16 +#define IKEV2_NONCE_MAX_LEN 256 + +/* IKE Header - RFC 4306, Sect. 3.1 */ +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#define IKEV2_SPI_LEN 8 + +struct ikev2_hdr { + u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */ + u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */ + u8 next_payload; + u8 version; /* MjVer | MnVer */ + u8 exchange_type; + u8 flags; + u8 message_id[4]; + u8 length[4]; /* total length of HDR + payloads */ +} STRUCT_PACKED; + +struct ikev2_payload_hdr { + u8 next_payload; + u8 flags; + u8 payload_length[2]; /* this payload, including the payload header */ +} STRUCT_PACKED; + +struct ikev2_proposal { + u8 type; /* 0 (last) or 2 (more) */ + u8 reserved; + u8 proposal_length[2]; /* including all transform and attributes */ + u8 proposal_num; + u8 protocol_id; /* IKEV2_PROTOCOL_* */ + u8 spi_size; + u8 num_transforms; + /* SPI of spi_size octets */ + /* Transforms */ +} STRUCT_PACKED; + +struct ikev2_transform { + u8 type; /* 0 (last) or 3 (more) */ + u8 reserved; + u8 transform_length[2]; /* including Header and Attributes */ + u8 transform_type; + u8 reserved2; + u8 transform_id[2]; + /* Transform Attributes */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* Current IKEv2 version from RFC 4306 */ +#define IKEV2_MjVer 2 +#define IKEV2_MnVer 0 +#ifdef CCNS_PL +#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4)) +#else /* CCNS_PL */ +#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer)) +#endif /* CCNS_PL */ + +/* IKEv2 Exchange Types */ +enum { + /* 0-33 RESERVED */ + IKE_SA_INIT = 34, + IKE_SA_AUTH = 35, + CREATE_CHILD_SA = 36, + INFORMATION = 37 + /* 38-239 RESERVED TO IANA */ + /* 240-255 Reserved for private use */ +}; + +/* IKEv2 Flags */ +#define IKEV2_HDR_INITIATOR 0x08 +#define IKEV2_HDR_VERSION 0x10 +#define IKEV2_HDR_RESPONSE 0x20 + +/* Payload Header Flags */ +#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01 + + +/* EAP-IKEv2 Payload Types (in Next Payload Type field) + * http://www.iana.org/assignments/eap-ikev2-payloads */ +enum { + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0, + IKEV2_PAYLOAD_SA = 33, + IKEV2_PAYLOAD_KEY_EXCHANGE = 34, + IKEV2_PAYLOAD_IDi = 35, + IKEV2_PAYLOAD_IDr = 36, + IKEV2_PAYLOAD_CERTIFICATE = 37, + IKEV2_PAYLOAD_CERT_REQ = 38, + IKEV2_PAYLOAD_AUTHENTICATION = 39, + IKEV2_PAYLOAD_NONCE = 40, + IKEV2_PAYLOAD_NOTIFICATION = 41, + IKEV2_PAYLOAD_VENDOD_ID = 43, + IKEV2_PAYLOAD_ENCRYPTED = 46, + IKEV2_PAYLOAD_NEXT_FAST_ID = 121 +}; + + +/* IKEv2 Proposal - Protocol ID */ +enum { + IKEV2_PROTOCOL_RESERVED = 0, + IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */ + IKEV2_PROTOCOL_AH = 2, + IKEV2_PROTOCOL_ESP = 3 +}; + + +/* IKEv2 Transform Types */ +enum { + IKEV2_TRANSFORM_ENCR = 1, + IKEV2_TRANSFORM_PRF = 2, + IKEV2_TRANSFORM_INTEG = 3, + IKEV2_TRANSFORM_DH = 4, + IKEV2_TRANSFORM_ESN = 5 +}; + +/* IKEv2 Tranform Type 1 (Encryption Algorithm) */ +enum { + ENCR_DES_IV64 = 1, + ENCR_DES = 2, + ENCR_3DES = 3, + ENCR_RC5 = 4, + ENCR_IDEA = 5, + ENCR_CAST = 6, + ENCR_BLOWFISH = 7, + ENCR_3IDEA = 8, + ENCR_DES_IV32 = 9, + ENCR_NULL = 11, + ENCR_AES_CBC = 12, + ENCR_AES_CTR = 13 +}; + +/* IKEv2 Transform Type 2 (Pseudo-random Function) */ +enum { + PRF_HMAC_MD5 = 1, + PRF_HMAC_SHA1 = 2, + PRF_HMAC_TIGER = 3, + PRF_AES128_XCBC = 4 +}; + +/* IKEv2 Transform Type 3 (Integrity Algorithm) */ +enum { + AUTH_HMAC_MD5_96 = 1, + AUTH_HMAC_SHA1_96 = 2, + AUTH_DES_MAC = 3, + AUTH_KPDK_MD5 = 4, + AUTH_AES_XCBC_96 = 5 +}; + +/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */ +enum { + DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */ + DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */ + DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */ + DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */ + DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */ + DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */ + DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */ + DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */ +}; + + +/* Identification Data Types (RFC 4306, Sect. 3.5) */ +enum { + ID_IPV4_ADDR = 1, + ID_FQDN = 2, + ID_RFC822_ADDR = 3, + ID_IPV6_ADDR = 5, + ID_DER_ASN1_DN = 9, + ID_DER_ASN1_GN= 10, + ID_KEY_ID = 11 +}; + + +/* Certificate Encoding (RFC 4306, Sect. 3.6) */ +enum { + CERT_ENCODING_PKCS7_X509 = 1, + CERT_ENCODING_PGP_CERT = 2, + CERT_ENCODING_DNS_SIGNED_KEY = 3, + /* X.509 Certificate - Signature: DER encoded X.509 certificate whose + * public key is used to validate the sender's AUTH payload */ + CERT_ENCODING_X509_CERT_SIGN = 4, + CERT_ENCODING_KERBEROS_TOKEN = 6, + /* DER encoded X.509 certificate revocation list */ + CERT_ENCODING_CRL = 7, + CERT_ENCODING_ARL = 8, + CERT_ENCODING_SPKI_CERT = 9, + CERT_ENCODING_X509_CERT_ATTR = 10, + /* PKCS #1 encoded RSA key */ + CERT_ENCODING_RAW_RSA_KEY = 11, + CERT_ENCODING_HASH_AND_URL_X509_CERT = 12, + CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13 +}; + + +/* Authentication Method (RFC 4306, Sect. 3.8) */ +enum { + AUTH_RSA_SIGN = 1, + AUTH_SHARED_KEY_MIC = 2, + AUTH_DSS_SIGN = 3 +}; + + +/* Notify Message Types (RFC 4306, Sect. 3.10.1) */ +enum { + UNSUPPORTED_CRITICAL_PAYLOAD = 1, + INVALID_IKE_SPI = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_SYNTAX = 7, + INVALID_MESSAGE_ID = 9, + INVALID_SPI = 11, + NO_PROPOSAL_CHOSEN = 14, + INVALID_KE_PAYLOAD = 17, + AUTHENTICATION_FAILED = 24, + SINGLE_PAIR_REQUIRED = 34, + NO_ADDITIONAL_SAS = 35, + INTERNAL_ADDRESS_FAILURE = 36, + FAILED_CP_REQUIRED = 37, + TS_UNACCEPTABLE = 38, + INVALID_SELECTORS = 39 +}; + + +struct ikev2_keys { + u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr; + size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len; +}; + + +int ikev2_keys_set(struct ikev2_keys *keys); +void ikev2_free_keys(struct ikev2_keys *keys); + + +/* Maximum hash length for supported hash algorithms */ +#define IKEV2_MAX_HASH_LEN 20 + +struct ikev2_integ_alg { + int id; + size_t key_len; + size_t hash_len; +}; + +struct ikev2_prf_alg { + int id; + size_t key_len; + size_t hash_len; +}; + +struct ikev2_encr_alg { + int id; + size_t key_len; + size_t block_size; +}; + +const struct ikev2_integ_alg * ikev2_get_integ(int id); +int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *hash); +const struct ikev2_prf_alg * ikev2_get_prf(int id); +int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *hash); +int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, + u8 *out, size_t out_len); +const struct ikev2_encr_alg * ikev2_get_encr(int id); +int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *plain, u8 *crypt, size_t len); +int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *crypt, u8 *plain, size_t len); + +int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, + const u8 *ID, size_t ID_len, u8 ID_type, + struct ikev2_keys *keys, int initiator, + const u8 *shared_secret, size_t shared_secret_len, + const u8 *nonce, size_t nonce_len, + const u8 *key_pad, size_t key_pad_len, + u8 *auth_data); + + +struct ikev2_payloads { + const u8 *sa; + size_t sa_len; + const u8 *ke; + size_t ke_len; + const u8 *idi; + size_t idi_len; + const u8 *idr; + size_t idr_len; + const u8 *cert; + size_t cert_len; + const u8 *auth; + size_t auth_len; + const u8 *nonce; + size_t nonce_len; + const u8 *encrypted; + size_t encrypted_len; + u8 encr_next_payload; + const u8 *notification; + size_t notification_len; +}; + +int ikev2_parse_payloads(struct ikev2_payloads *payloads, + u8 next_payload, const u8 *pos, const u8 *end); + +u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, const struct ikev2_hdr *hdr, + const u8 *encrypted, size_t encrypted_len, + size_t *res_len); +void ikev2_update_hdr(struct wpabuf *msg); +int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, struct wpabuf *msg, + struct wpabuf *plain, u8 next_payload); +int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, + const struct ikev2_integ_alg *integ, + const struct ikev2_encr_alg *encr, + const u8 *skeyseed, const u8 *data, size_t data_len, + struct ikev2_keys *keys); + +#endif /* IKEV2_COMMON_H */ diff --git a/src/eap_peer/.gitignore b/src/eap_peer/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/eap_peer/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile new file mode 100644 index 0000000000000..d9449a28da42d --- /dev/null +++ b/src/eap_peer/Makefile @@ -0,0 +1,12 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.so *.d + +install: + if ls *.so >/dev/null 2>&1; then \ + install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \ + cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \ + ; fi diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c new file mode 100644 index 0000000000000..e8e504af54956 --- /dev/null +++ b/src/eap_peer/eap.c @@ -0,0 +1,2075 @@ +/* + * EAP peer state machines (RFC 4137) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements the Peer State Machine as defined in RFC 4137. The used + * states and state transitions match mostly with the RFC. However, there are + * couple of additional transitions for working around small issues noticed + * during testing. These exceptions are explained in comments within the + * functions in this file. The method functions, m.func(), are similar to the + * ones used in RFC 4137, but some small changes have used here to optimize + * operations and to add functionality needed for fast re-authentication + * (session resumption). + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_config.h" +#include "tls.h" +#include "crypto.h" +#include "pcsc_funcs.h" +#include "wpa_ctrl.h" +#include "state_machine.h" +#include "eap_common/eap_wsc_common.h" + +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" + +#define EAP_MAX_AUTH_ROUNDS 50 + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, + EapType method); +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); +static void eap_sm_processIdentity(struct eap_sm *sm, + const struct wpabuf *req); +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); +static struct wpabuf * eap_sm_buildNotify(int id); +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state); +static const char * eap_sm_decision_txt(EapDecision decision); +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) +{ + return sm->eapol_cb->get_int(sm->eapol_ctx, var); +} + + +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, + unsigned int value) +{ + sm->eapol_cb->set_int(sm->eapol_ctx, var, value); +} + + +static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) +{ + return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); +} + + +static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) +{ + if (sm->m == NULL || sm->eap_method_priv == NULL) + return; + + wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " + "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + + +/** + * eap_allowed_method - Check whether EAP method is allowed + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +{ + struct eap_peer_config *config = eap_get_config(sm); + int i; + struct eap_method_type *m; + + if (config == NULL || config->eap_methods == NULL) + return 1; + + m = config->eap_methods; + for (i = 0; m[i].vendor != EAP_VENDOR_IETF || + m[i].method != EAP_TYPE_NONE; i++) { + if (m[i].vendor == vendor && m[i].method == method) + return 1; + } + return 0; +} + + +/* + * This state initializes state machine variables when the machine is + * activated (portEnabled = TRUE). This is also used when re-starting + * authentication (eapRestart == TRUE). + */ +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv) && + !sm->prev_failure) { + wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " + "fast reauthentication"); + sm->m->deinit_for_reauth(sm, sm->eap_method_priv); + } else { + eap_deinit_prev_method(sm, "INITIALIZE"); + } + sm->selectedMethod = EAP_TYPE_NONE; + sm->methodState = METHOD_NONE; + sm->allowNotifications = TRUE; + sm->decision = DECISION_FAIL; + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); + eapol_set_bool(sm, EAPOL_eapFail, FALSE); + os_free(sm->eapKeyData); + sm->eapKeyData = NULL; + sm->eapKeyAvailable = FALSE; + eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + sm->lastId = -1; /* new session - make sure this does not match with + * the first EAP-Packet */ + /* + * RFC 4137 does not reset eapResp and eapNoResp here. However, this + * seemed to be able to trigger cases where both were set and if EAPOL + * state machine uses eapNoResp first, it may end up not sending a real + * reply correctly. This occurred when the workaround in FAIL state set + * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do + * something else(?) + */ + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + sm->num_rounds = 0; + sm->prev_failure = 0; +} + + +/* + * This state is reached whenever service from the lower layer is interrupted + * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE + * occurs when the port becomes enabled. + */ +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; +} + + +/* + * The state machine spends most of its time here, waiting for something to + * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and + * SEND_RESPONSE states. + */ +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); +} + + +/* + * This state is entered when an EAP packet is received (eapReq == TRUE) to + * parse the packet header. + */ +SM_STATE(EAP, RECEIVED) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, RECEIVED); + eapReqData = eapol_get_eapReqData(sm); + /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ + eap_sm_parseEapReq(sm, eapReqData); + sm->num_rounds++; +} + + +/* + * This state is entered when a request for a new type comes in. Either the + * correct method is started, or a Nak response is built. + */ +SM_STATE(EAP, GET_METHOD) +{ + int reinit; + EapType method; + + SM_ENTRY(EAP, GET_METHOD); + + if (sm->reqMethod == EAP_TYPE_EXPANDED) + method = sm->reqVendorMethod; + else + method = sm->reqMethod; + + if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { + wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", + sm->reqVendor, method); + goto nak; + } + + /* + * RFC 4137 does not define specific operation for fast + * re-authentication (session resumption). The design here is to allow + * the previously used method data to be maintained for + * re-authentication if the method support session resumption. + * Otherwise, the previously used method data is freed and a new method + * is allocated here. + */ + if (sm->fast_reauth && + sm->m && sm->m->vendor == sm->reqVendor && + sm->m->method == method && + sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: Using previous method data" + " for fast re-authentication"); + reinit = 1; + } else { + eap_deinit_prev_method(sm, "GET_METHOD"); + reinit = 0; + } + + sm->selectedMethod = sm->reqMethod; + if (sm->m == NULL) + sm->m = eap_peer_get_eap_method(sm->reqVendor, method); + if (!sm->m) { + wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " + "vendor %d method %d", + sm->reqVendor, method); + goto nak; + } + + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " + "vendor %u method %u (%s)", + sm->reqVendor, method, sm->m->name); + if (reinit) + sm->eap_method_priv = sm->m->init_for_reauth( + sm, sm->eap_method_priv); + else + sm->eap_method_priv = sm->m->init(sm); + + if (sm->eap_method_priv == NULL) { + struct eap_peer_config *config = eap_get_config(sm); + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP: Failed to initialize EAP method: vendor %u " + "method %u (%s)", + sm->reqVendor, method, sm->m->name); + sm->m = NULL; + sm->methodState = METHOD_NONE; + sm->selectedMethod = EAP_TYPE_NONE; + if (sm->reqMethod == EAP_TYPE_TLS && config && + (config->pending_req_pin || + config->pending_req_passphrase)) { + /* + * Return without generating Nak in order to allow + * entering of PIN code or passphrase to retry the + * current EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " + "request - skip Nak"); + return; + } + + goto nak; + } + + sm->methodState = METHOD_INIT; + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD + "EAP vendor %u method %u (%s) selected", + sm->reqVendor, method, sm->m->name); + return; + +nak: + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); +} + + +/* + * The method processing happens here. The request from the authenticator is + * processed, and an appropriate response packet is built. + */ +SM_STATE(EAP, METHOD) +{ + struct wpabuf *eapReqData; + struct eap_method_ret ret; + + SM_ENTRY(EAP, METHOD); + if (sm->m == NULL) { + wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); + return; + } + + eapReqData = eapol_get_eapReqData(sm); + + /* + * Get ignore, methodState, decision, allowNotifications, and + * eapRespData. RFC 4137 uses three separate method procedure (check, + * process, and buildResp) in this state. These have been combined into + * a single function call to m->process() in order to optimize EAP + * method implementation interface a bit. These procedures are only + * used from within this METHOD state, so there is no need to keep + * these as separate C functions. + * + * The RFC 4137 procedures return values as follows: + * ignore = m.check(eapReqData) + * (methodState, decision, allowNotifications) = m.process(eapReqData) + * eapRespData = m.buildResp(reqId) + */ + os_memset(&ret, 0, sizeof(ret)); + ret.ignore = sm->ignore; + ret.methodState = sm->methodState; + ret.decision = sm->decision; + ret.allowNotifications = sm->allowNotifications; + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, + eapReqData); + wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " + "methodState=%s decision=%s", + ret.ignore ? "TRUE" : "FALSE", + eap_sm_method_state_txt(ret.methodState), + eap_sm_decision_txt(ret.decision)); + + sm->ignore = ret.ignore; + if (sm->ignore) + return; + sm->methodState = ret.methodState; + sm->decision = ret.decision; + sm->allowNotifications = ret.allowNotifications; + + if (sm->m->isKeyAvailable && sm->m->getKey && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { + os_free(sm->eapKeyData); + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + } +} + + +/* + * This state signals the lower layer that a response packet is ready to be + * sent. + */ +SM_STATE(EAP, SEND_RESPONSE) +{ + SM_ENTRY(EAP, SEND_RESPONSE); + wpabuf_free(sm->lastRespData); + if (sm->eapRespData) { + if (sm->workaround) + os_memcpy(sm->last_md5, sm->req_md5, 16); + sm->lastId = sm->reqId; + sm->lastRespData = wpabuf_dup(sm->eapRespData); + eapol_set_bool(sm, EAPOL_eapResp, TRUE); + } else + sm->lastRespData = NULL; + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); +} + + +/* + * This state signals the lower layer that the request was discarded, and no + * response packet will be sent at this time. + */ +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +/* + * Handles requests for Identity method and builds a response. + */ +SM_STATE(EAP, IDENTITY) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, IDENTITY); + eapReqData = eapol_get_eapReqData(sm); + eap_sm_processIdentity(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); +} + + +/* + * Handles requests for Notification method and builds a response. + */ +SM_STATE(EAP, NOTIFICATION) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, NOTIFICATION); + eapReqData = eapol_get_eapReqData(sm); + eap_sm_processNotify(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNotify(sm->reqId); +} + + +/* + * This state retransmits the previous response packet. + */ +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + wpabuf_free(sm->eapRespData); + if (sm->lastRespData) + sm->eapRespData = wpabuf_dup(sm->lastRespData); + else + sm->eapRespData = NULL; +} + + +/* + * This state is entered in case of a successful completion of authentication + * and state machine waits here until port is disabled or EAP authentication is + * restarted. + */ +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here, but this seems to be required + * to get EAPOL Supplicant backend state machine into SUCCESS state. In + * addition, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully"); +} + + +/* + * This state is entered in case of a failure and state machine waits here + * until port is disabled or EAP authentication is restarted. + */ +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here. However, either eapResp or + * eapNoResp is required to be set after processing the received EAP + * frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); + + sm->prev_failure = 1; +} + + +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) +{ + /* + * At least Microsoft IAS and Meetinghouse Aegis seem to be sending + * EAP-Success/Failure with lastId + 1 even though RFC 3748 and + * RFC 4137 require that reqId == lastId. In addition, it looks like + * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. + * + * Accept this kind of Id if EAP workarounds are enabled. These are + * unauthenticated plaintext messages, so this should have minimal + * security implications (bit easier to fake EAP-Success/Failure). + */ + if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || + reqId == ((lastId + 2) & 0xff))) { + wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " + "identifier field in EAP Success: " + "reqId=%d lastId=%d (these are supposed to be " + "same)", reqId, lastId); + return 1; + } + wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " + "lastId=%d", reqId, lastId); + return 0; +} + + +/* + * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions + */ + +static void eap_peer_sm_step_idle(struct eap_sm *sm) +{ + /* + * The first three transitions are from RFC 4137. The last two are + * local additions to handle special cases with LEAP and PEAP server + * not sending EAP-Success in some cases. + */ + if (eapol_get_bool(sm, EAPOL_eapReq)) + SM_ENTER(EAP, RECEIVED); + else if ((eapol_get_bool(sm, EAPOL_altAccept) && + sm->decision != DECISION_FAIL) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision == DECISION_UNCOND_SUCC)) + SM_ENTER(EAP, SUCCESS); + else if (eapol_get_bool(sm, EAPOL_altReject) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision != DECISION_UNCOND_SUCC) || + (eapol_get_bool(sm, EAPOL_altAccept) && + sm->methodState != METHOD_CONT && + sm->decision == DECISION_FAIL)) + SM_ENTER(EAP, FAILURE); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + sm->leap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + else if (sm->selectedMethod == EAP_TYPE_PEAP && + sm->peap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); +} + + +static int eap_peer_req_is_duplicate(struct eap_sm *sm) +{ + int duplicate; + + duplicate = (sm->reqId == sm->lastId) && sm->rxReq; + if (sm->workaround && duplicate && + os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + /* + * RFC 4137 uses (reqId == lastId) as the only verification for + * duplicate EAP requests. However, this misses cases where the + * AS is incorrectly using the same id again; and + * unfortunately, such implementations exist. Use MD5 hash as + * an extra verification for the packets being duplicate to + * workaround these issues. + */ + wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but " + "EAP packets were not identical"); + wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a " + "duplicate packet"); + duplicate = 0; + } + + return duplicate; +} + + +static void eap_peer_sm_step_received(struct eap_sm *sm) +{ + int duplicate = eap_peer_req_is_duplicate(sm); + + /* + * Two special cases below for LEAP are local additions to work around + * odd LEAP behavior (EAP-Success in the middle of authentication and + * then swapped roles). Other transitions are based on RFC 4137. + */ + if (sm->rxSuccess && sm->decision != DECISION_FAIL && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, SUCCESS); + else if (sm->methodState != METHOD_CONT && + ((sm->rxFailure && + sm->decision != DECISION_UNCOND_SUCC) || + (sm->rxSuccess && sm->decision == DECISION_FAIL && + (sm->selectedMethod != EAP_TYPE_LEAP || + sm->methodState != METHOD_MAY_CONT))) && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, FAILURE); + else if (sm->rxReq && duplicate) + SM_ENTER(EAP, RETRANSMIT); + else if (sm->rxReq && !duplicate && + sm->reqMethod == EAP_TYPE_NOTIFICATION && + sm->allowNotifications) + SM_ENTER(EAP, NOTIFICATION); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod == EAP_TYPE_IDENTITY) + SM_ENTER(EAP, IDENTITY); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod != EAP_TYPE_IDENTITY && + sm->reqMethod != EAP_TYPE_NOTIFICATION) + SM_ENTER(EAP, GET_METHOD); + else if (sm->rxReq && !duplicate && + sm->reqMethod == sm->selectedMethod && + sm->methodState != METHOD_DONE) + SM_ENTER(EAP, METHOD); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + (sm->rxSuccess || sm->rxResp)) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, DISCARD); +} + + +static void eap_peer_sm_step_local(struct eap_sm *sm) +{ + switch (sm->EAP_state) { + case EAP_INITIALIZE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISABLED: + if (eapol_get_bool(sm, EAPOL_portEnabled) && + !sm->force_disabled) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + eap_peer_sm_step_idle(sm); + break; + case EAP_RECEIVED: + eap_peer_sm_step_received(sm); + break; + case EAP_GET_METHOD: + if (sm->selectedMethod == sm->reqMethod) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_METHOD: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SEND_RESPONSE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_IDENTITY: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_NOTIFICATION: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_RETRANSMIT: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SUCCESS: + break; + case EAP_FAILURE: + break; + } +} + + +SM_STEP(EAP) +{ + /* Global transitions */ + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + /* RFC 4137 does not place any limit on number of EAP messages + * in an authentication session. However, some error cases have + * ended up in a state were EAP messages were sent between the + * peer and server in a loop (e.g., TLS ACK frame in both + * direction). Since this is quite undesired outcome, limit the + * total number of EAP round-trips and abort authentication if + * this limit is exceeded. + */ + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else { + /* Local transitions */ + eap_peer_sm_step_local(sm); + } +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, + EapType method) +{ + if (!eap_allowed_method(sm, vendor, method)) { + wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " + "vendor %u method %u", vendor, method); + return FALSE; + } + if (eap_peer_get_eap_method(vendor, method)) + return TRUE; + wpa_printf(MSG_DEBUG, "EAP: not included in build: " + "vendor %u method %u", vendor, method); + return FALSE; +} + + +static struct wpabuf * eap_sm_build_expanded_nak( + struct eap_sm *sm, int id, const struct eap_method *methods, + size_t count) +{ + struct wpabuf *resp; + int found = 0; + const struct eap_method *m; + + wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); + + /* RFC 3748 - 5.3.2: Expanded Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, + 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NAK); + + for (m = methods; m; m = m->next) { + if (sm->reqVendor == m->vendor && + sm->reqVendorMethod == m->method) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + wpa_printf(MSG_DEBUG, "EAP: allowed type: " + "vendor=%u method=%u", + m->vendor, m->method); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, m->vendor); + wpabuf_put_be32(resp, m->method); + + found++; + } + } + if (!found) { + wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NONE); + } + + eap_update_len(resp); + + return resp; +} + + +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) +{ + struct wpabuf *resp; + u8 *start; + int found = 0, expanded_found = 0; + size_t count; + const struct eap_method *methods, *m; + + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " + "vendor=%u method=%u not allowed)", sm->reqMethod, + sm->reqVendor, sm->reqVendorMethod); + methods = eap_peer_get_methods(&count); + if (methods == NULL) + return NULL; + if (sm->reqMethod == EAP_TYPE_EXPANDED) + return eap_sm_build_expanded_nak(sm, id, methods, count); + + /* RFC 3748 - 5.3.1: Legacy Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, + sizeof(struct eap_hdr) + 1 + count + 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + start = wpabuf_put(resp, 0); + for (m = methods; m; m = m->next) { + if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + if (m->vendor != EAP_VENDOR_IETF) { + if (expanded_found) + continue; + expanded_found = 1; + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + } else + wpabuf_put_u8(resp, m->method); + found++; + } + } + if (!found) + wpabuf_put_u8(resp, EAP_TYPE_NONE); + wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); + + eap_update_len(resp); + + return resp; +} + + +static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) +{ + const struct eap_hdr *hdr = wpabuf_head(req); + const u8 *pos = (const u8 *) (hdr + 1); + pos++; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + "EAP authentication started"); + + /* + * RFC 3748 - 5.1: Identity + * Data field may contain a displayable message in UTF-8. If this + * includes NUL-character, only the data before that should be + * displayed. Some EAP implementasitons may piggy-back additional + * options after the NUL. + */ + /* TODO: could save displayable message so that it can be shown to the + * user in case of interaction is required */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", + pos, be_to_host16(hdr->length) - 5); +} + + +#ifdef PCSC_FUNCS +static int eap_sm_imsi_identity(struct eap_sm *sm, + struct eap_peer_config *conf) +{ + int aka = 0; + char imsi[100]; + size_t imsi_len; + struct eap_method_type *m = conf->eap_methods; + int i; + + imsi_len = sizeof(imsi); + if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || + m[i].method != EAP_TYPE_NONE); i++) { + if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA) { + aka = 1; + break; + } + } + + os_free(conf->identity); + conf->identity = os_malloc(1 + imsi_len); + if (conf->identity == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate buffer for " + "IMSI-based identity"); + return -1; + } + + conf->identity[0] = aka ? '0' : '1'; + os_memcpy(conf->identity + 1, imsi, imsi_len); + conf->identity_len = 1 + imsi_len; + + return 0; +} +#endif /* PCSC_FUNCS */ + + +static int eap_sm_set_scard_pin(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (scard_set_pin(sm->scard_ctx, conf->pin)) { + /* + * Make sure the same PIN is not tried again in order to avoid + * blocking SIM. + */ + os_free(conf->pin); + conf->pin = NULL; + + wpa_printf(MSG_WARNING, "PIN validation failed"); + eap_sm_request_pin(sm); + return -1; + } + return 0; +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + +static int eap_sm_get_scard_identity(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (eap_sm_set_scard_pin(sm, conf)) + return -1; + + return eap_sm_imsi_identity(sm, conf); +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + + +/** + * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: EAP identifier for the packet + * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) + * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on + * failure + * + * This function allocates and builds an EAP-Identity/Response packet for the + * current network. The caller is responsible for freeing the returned data. + */ +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf *resp; + const u8 *identity; + size_t identity_len; + + if (config == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " + "was not available"); + return NULL; + } + + if (sm->m && sm->m->get_identity && + (identity = sm->m->get_identity(sm, sm->eap_method_priv, + &identity_len)) != NULL) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " + "identity", identity, identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", + identity, identity_len); + } else { + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", + identity, identity_len); + } + + if (identity == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " + "configuration was not available"); + if (config->pcsc) { + if (eap_sm_get_scard_identity(sm, config) < 0) + return NULL; + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", identity, identity_len); + } else { + eap_sm_request_identity(sm); + return NULL; + } + } else if (config->pcsc) { + if (eap_sm_set_scard_pin(sm, config) < 0) + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_data(resp, identity, identity_len); + + return resp; +} + + +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) +{ + const u8 *pos; + char *msg; + size_t i, msg_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, + &msg_len); + if (pos == NULL) + return; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", + pos, msg_len); + + msg = os_malloc(msg_len + 1); + if (msg == NULL) + return; + for (i = 0; i < msg_len; i++) + msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; + msg[msg_len] = '\0'; + wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", + WPA_EVENT_EAP_NOTIFICATION, msg); + os_free(msg); +} + + +static struct wpabuf * eap_sm_buildNotify(int id) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + return resp; +} + + +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) +{ + const struct eap_hdr *hdr; + size_t plen; + const u8 *pos; + + sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE; + sm->reqId = 0; + sm->reqMethod = EAP_TYPE_NONE; + sm->reqVendor = EAP_VENDOR_IETF; + sm->reqVendorMethod = EAP_TYPE_NONE; + + if (req == NULL || wpabuf_len(req) < sizeof(*hdr)) + return; + + hdr = wpabuf_head(req); + plen = be_to_host16(hdr->length); + if (plen > wpabuf_len(req)) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) wpabuf_len(req), + (unsigned long) plen); + return; + } + + sm->reqId = hdr->identifier; + + if (sm->workaround) { + const u8 *addr[1]; + addr[0] = wpabuf_head(req); + md5_vector(1, addr, &plen, sm->req_md5); + } + + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " + "no Type field"); + return; + } + sm->rxReq = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos++; + if (sm->reqMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->reqVendor = WPA_GET_BE24(pos); + pos += 3; + sm->reqVendorMethod = WPA_GET_BE32(pos); + } + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " + "method=%u vendor=%u vendorMethod=%u", + sm->reqId, sm->reqMethod, sm->reqVendor, + sm->reqVendorMethod); + break; + case EAP_CODE_RESPONSE: + if (sm->selectedMethod == EAP_TYPE_LEAP) { + /* + * LEAP differs from RFC 4137 by using reversed roles + * for mutual authentication and because of this, we + * need to accept EAP-Response frames if LEAP is used. + */ + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short " + "EAP-Response - no Type field"); + return; + } + sm->rxResp = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos; + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " + "LEAP method=%d id=%d", + sm->reqMethod, sm->reqId); + break; + } + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + sm->rxSuccess = TRUE; + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + sm->rxFailure = TRUE; + break; + default: + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " + "code %d", hdr->code); + break; + } +} + + +/** + * eap_peer_sm_init - Allocate and initialize EAP peer state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @msg_ctx: Context data for wpa_msg() calls + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. In addition, + * this initializes TLS library for the new EAP state machine. eapol_cb pointer + * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP + * state machine. Consequently, the caller must make sure that this data + * structure remains alive while the EAP state machine is active. + */ +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf) +{ + struct eap_sm *sm; + struct tls_config tlsconf; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->msg_ctx = msg_ctx; + sm->ClientTimeout = 60; + sm->wps = conf->wps; + + os_memset(&tlsconf, 0, sizeof(tlsconf)); + tlsconf.opensc_engine_path = conf->opensc_engine_path; + tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; + tlsconf.pkcs11_module_path = conf->pkcs11_module_path; + sm->ssl_ctx = tls_init(&tlsconf); + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " + "context."); + os_free(sm); + return NULL; + } + + return sm; +} + + +/** + * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ +void eap_peer_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + eap_deinit_prev_method(sm, "EAP deinit"); + eap_sm_abort(sm); + tls_deinit(sm->ssl_ctx); + os_free(sm); +} + + +/** + * eap_peer_sm_step - Step EAP peer state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ +int eap_peer_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +/** + * eap_sm_abort - Abort EAP authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Release system resources that have been allocated for the authentication + * session without fully deinitializing the EAP state machine. + */ +void eap_sm_abort(struct eap_sm *sm) +{ + wpabuf_free(sm->lastRespData); + sm->lastRespData = NULL; + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + os_free(sm->eapKeyData); + sm->eapKeyData = NULL; + + /* This is not clearly specified in the EAP statemachines draft, but + * it seems necessary to make sure that some of the EAPOL variables get + * cleared for the next authentication. */ + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); +} + + +#ifdef CONFIG_CTRL_IFACE +static const char * eap_sm_state_txt(int state) +{ + switch (state) { + case EAP_INITIALIZE: + return "INITIALIZE"; + case EAP_DISABLED: + return "DISABLED"; + case EAP_IDLE: + return "IDLE"; + case EAP_RECEIVED: + return "RECEIVED"; + case EAP_GET_METHOD: + return "GET_METHOD"; + case EAP_METHOD: + return "METHOD"; + case EAP_SEND_RESPONSE: + return "SEND_RESPONSE"; + case EAP_DISCARD: + return "DISCARD"; + case EAP_IDENTITY: + return "IDENTITY"; + case EAP_NOTIFICATION: + return "NOTIFICATION"; + case EAP_RETRANSMIT: + return "RETRANSMIT"; + case EAP_SUCCESS: + return "SUCCESS"; + case EAP_FAILURE: + return "FAILURE"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state) +{ + switch (state) { + case METHOD_NONE: + return "NONE"; + case METHOD_INIT: + return "INIT"; + case METHOD_CONT: + return "CONT"; + case METHOD_MAY_CONT: + return "MAY_CONT"; + case METHOD_DONE: + return "DONE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_decision_txt(EapDecision decision) +{ + switch (decision) { + case DECISION_FAIL: + return "FAIL"; + case DECISION_COND_SUCC: + return "COND_SUCC"; + case DECISION_UNCOND_SUCC: + return "UNCOND_SUCC"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_CTRL_IFACE + +/** + * eap_sm_get_status - Get EAP state machine status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAP state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) +{ + int len, ret; + + if (sm == NULL) + return 0; + + len = os_snprintf(buf, buflen, + "EAP state=%s\n", + eap_sm_state_txt(sm->EAP_state)); + if (len < 0 || (size_t) len >= buflen) + return 0; + + if (sm->selectedMethod != EAP_TYPE_NONE) { + const char *name; + if (sm->m) { + name = sm->m->name; + } else { + const struct eap_method *m = + eap_peer_get_eap_method(EAP_VENDOR_IETF, + sm->selectedMethod); + if (m) + name = m->name; + else + name = "?"; + } + ret = os_snprintf(buf + len, buflen - len, + "selectedMethod=%d (EAP-%s)\n", + sm->selectedMethod, name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + if (sm->m && sm->m->get_status) { + len += sm->m->get_status(sm, sm->eap_method_priv, + buf + len, buflen - len, + verbose); + } + } + + if (verbose) { + ret = os_snprintf(buf + len, buflen - len, + "reqMethod=%d\n" + "methodState=%s\n" + "decision=%s\n" + "ClientTimeout=%d\n", + sm->reqMethod, + eap_sm_method_state_txt(sm->methodState), + eap_sm_decision_txt(sm->decision), + sm->ClientTimeout); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +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, eap_ctrl_req_type type, + const char *msg, size_t msglen) +{ + struct eap_peer_config *config; + char *field, *txt, *tmp; + + if (sm == NULL) + return; + config = eap_get_config(sm); + if (config == NULL) + return; + + switch (type) { + case TYPE_IDENTITY: + field = "IDENTITY"; + txt = "Identity"; + config->pending_req_identity++; + break; + case TYPE_PASSWORD: + field = "PASSWORD"; + 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) { + tmp = os_malloc(msglen + 3); + if (tmp == NULL) + return; + tmp[0] = '['; + os_memcpy(tmp + 1, msg, msglen); + tmp[msglen + 1] = ']'; + tmp[msglen + 2] = '\0'; + txt = tmp; + os_free(config->pending_req_otp); + config->pending_req_otp = tmp; + config->pending_req_otp_len = msglen + 3; + } else { + if (config->pending_req_otp == NULL) + return; + txt = config->pending_req_otp; + } + break; + case TYPE_PASSPHRASE: + field = "PASSPHRASE"; + txt = "Private key passphrase"; + config->pending_req_passphrase++; + break; + default: + return; + } + + if (sm->eapol_cb->eap_param_needed) + sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eap_sm_request(sm, type, msg, msglen) do { } while (0) +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +/** + * eap_sm_request_identity - Request identity from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request identity information for the + * current network. This is normally called when the identity is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_identity(struct eap_sm *sm) +{ + eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); +} + + +/** + * eap_sm_request_password - Request password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request password information for the + * current network. This is normally called when the password is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_password(struct eap_sm *sm) +{ + eap_sm_request(sm, 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_peer_sm_init() + * + * EAP methods can call this function to request new password information for + * the current network. This is normally called when the EAP method indicates + * that the current password has expired and password change is required. The + * request will be sent to monitor programs through the control interface. + */ +void eap_sm_request_new_password(struct eap_sm *sm) +{ + eap_sm_request(sm, 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_peer_sm_init() + * + * EAP methods can call this function to request SIM or smart card PIN + * information for the current network. This is normally called when the PIN is + * not included in the network configuration. The request will be sent to + * monitor programs through the control interface. + */ +void eap_sm_request_pin(struct eap_sm *sm) +{ + eap_sm_request(sm, 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_peer_sm_init() + * @msg: Message to be displayed to the user when asking for OTP + * @msg_len: Length of the user displayable message + * + * EAP methods can call this function to request open time password (OTP) for + * the current network. The request will be sent to monitor programs through + * the control interface. + */ +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) +{ + eap_sm_request(sm, TYPE_OTP, msg, msg_len); +} + + +/** + * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request passphrase for a private key + * for the current network. This is normally called when the passphrase is not + * included in the network configuration. The request will be sent to monitor + * programs through the control interface. + */ +void eap_sm_request_passphrase(struct eap_sm *sm) +{ + eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); +} + + +/** + * eap_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ +void eap_sm_notify_ctrl_attached(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + if (config == NULL) + return; + + /* Re-send any pending requests for user data since a new control + * interface was added. This handles cases where the EAP authentication + * starts immediately after system startup when the user interface is + * not yet running. */ + if (config->pending_req_identity) + eap_sm_request_identity(sm); + if (config->pending_req_password) + eap_sm_request_password(sm); + if (config->pending_req_new_password) + eap_sm_request_new_password(sm); + if (config->pending_req_otp) + eap_sm_request_otp(sm, NULL, 0); + if (config->pending_req_pin) + eap_sm_request_pin(sm); + if (config->pending_req_passphrase) + eap_sm_request_passphrase(sm); +} + + +static int eap_allowed_phase2_type(int vendor, int type) +{ + if (vendor != EAP_VENDOR_IETF) + return 0; + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + + +/** + * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name + * @name: EAP method name, e.g., MD5 + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers that are allowed for + * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ +u32 eap_get_phase2_type(const char *name, int *vendor) +{ + int v; + u8 type = eap_peer_get_type(name, &v); + if (eap_allowed_phase2_type(v, type)) { + *vendor = v; + return type; + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_get_phase2_types - Get list of allowed EAP phase 2 types + * @config: Pointer to a network configuration + * @count: Pointer to a variable to be filled with number of returned EAP types + * Returns: Pointer to allocated type list or %NULL on failure + * + * This function generates an array of allowed EAP phase 2 (tunneled) types for + * the given network configuration. + */ +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count) +{ + struct eap_method_type *buf; + u32 method; + int vendor; + size_t mcount; + const struct eap_method *methods, *m; + + methods = eap_peer_get_methods(&mcount); + if (methods == NULL) + return NULL; + *count = 0; + buf = os_malloc(mcount * sizeof(struct eap_method_type)); + if (buf == NULL) + return NULL; + + for (m = methods; m; m = m->next) { + vendor = m->vendor; + method = m->method; + if (eap_allowed_phase2_type(vendor, method)) { + if (vendor == EAP_VENDOR_IETF && + method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count].vendor = vendor; + buf[*count].method = method; + (*count)++; + } + } + + return buf; +} + + +/** + * eap_set_fast_reauth - Update fast_reauth setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled + */ +void eap_set_fast_reauth(struct eap_sm *sm, int enabled) +{ + sm->fast_reauth = enabled; +} + + +/** + * eap_set_workaround - Update EAP workarounds setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds + */ +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) +{ + sm->workaround = workaround; +} + + +/** + * eap_get_config - Get current network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the current network configuration or %NULL if not found + * + * EAP peer methods should avoid using this function if they can use other + * access functions, like eap_get_config_identity() and + * eap_get_config_password(), that do not require direct access to + * struct eap_peer_config. + */ +struct eap_peer_config * eap_get_config(struct eap_sm *sm) +{ + return sm->eapol_cb->get_config(sm->eapol_ctx); +} + + +/** + * eap_get_config_identity - Get identity from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the identity + * Returns: Pointer to the identity or %NULL if not found + */ +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->identity_len; + return config->identity; +} + + +/** + * eap_get_config_password - Get password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the password + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->password_len; + return config->password; +} + + +/** + * eap_get_config_password2 - Get password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the password + * @hash: Buffer for returning whether the password is stored as a + * NtPasswordHash instead of plaintext password; can be %NULL if this + * information is not needed + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->password_len; + if (hash) + *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); + return config->password; +} + + +/** + * eap_get_config_new_password - Get new password from network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the new password + * Returns: Pointer to the new password or %NULL if not found + */ +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->new_password_len; + return config->new_password; +} + + +/** + * eap_get_config_otp - Get one-time password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the one-time password + * Returns: Pointer to the one-time password or %NULL if not found + */ +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->otp_len; + return config->otp; +} + + +/** + * eap_clear_config_otp - Clear used one-time password + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function clears a used one-time password (OTP) from the current network + * configuration. This should be called when the OTP has been used and is not + * needed anymore. + */ +void eap_clear_config_otp(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return; + os_memset(config->otp, 0, config->otp_len); + os_free(config->otp); + config->otp = NULL; + config->otp_len = 0; +} + + +/** + * eap_get_config_phase1 - Get phase1 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase1(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase1; +} + + +/** + * eap_get_config_phase2 - Get phase2 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase2(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase2; +} + + +/** + * eap_key_available - Get key availability (eapKeyAvailable variable) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: 1 if EAP keying material is available, 0 if not + */ +int eap_key_available(struct eap_sm *sm) +{ + return sm ? sm->eapKeyAvailable : 0; +} + + +/** + * eap_notify_success - Notify EAP state machine about external success trigger + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function is called when external event, e.g., successful completion of + * WPA-PSK key handshake, is indicating that EAP state machine should move to + * success state. This is mainly used with security modes that do not use EAP + * state machine (e.g., WPA-PSK). + */ +void eap_notify_success(struct eap_sm *sm) +{ + if (sm) { + sm->decision = DECISION_COND_SUCC; + sm->EAP_state = EAP_SUCCESS; + } +} + + +/** + * eap_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a lower layer has detected a successful + * authentication. This is used to recover from dropped EAP-Success messages. + */ +void eap_notify_lower_layer_success(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + if (eapol_get_bool(sm, EAPOL_eapSuccess) || + sm->decision == DECISION_FAIL || + (sm->methodState != METHOD_MAY_CONT && + sm->methodState != METHOD_DONE)) + return; + + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully (based on lower " + "layer success)"); +} + + +/** + * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the key + * Returns: Pointer to the EAP keying data or %NULL on failure + * + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The + * key is available only after a successful authentication. EAP state machine + * continues to manage the key data and the caller must not change or free the + * returned data. + */ +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen; + return sm->eapKeyData; +} + + +/** + * eap_get_eapKeyData - Get EAP response data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure + * + * Fetch EAP response (eapRespData) from the EAP state machine. This data is + * available when EAP state machine has processed an incoming EAP request. The + * EAP state machine does not maintain a reference to the response after this + * function is called and the caller is responsible for freeing the data. + */ +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm) +{ + struct wpabuf *resp; + + if (sm == NULL || sm->eapRespData == NULL) + return NULL; + + resp = sm->eapRespData; + sm->eapRespData = NULL; + + return resp; +} + + +/** + * eap_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAP state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) +{ + if (sm) + sm->scard_ctx = ctx; +} + + +/** + * eap_set_config_blob - Set or add a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_get_config_blob - Get a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, + const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_set_force_disabled - Set force_disabled flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @disabled: 1 = EAP disabled, 0 = EAP enabled + * + * This function is used to force EAP state machine to be disabled when it is + * not in use (e.g., with WPA-PSK or plaintext connections). + */ +void eap_set_force_disabled(struct eap_sm *sm, int disabled) +{ + sm->force_disabled = disabled; +} + + + /** + * eap_notify_pending - Notify that EAP method is ready to re-process a request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * An EAP method can perform a pending operation (e.g., to get a response from + * an external process). Once the response is available, this function can be + * used to request EAPOL state machine to retry delivering the previously + * received (and still unanswered) EAP request to EAP state machine. + */ +void eap_notify_pending(struct eap_sm *sm) +{ + sm->eapol_cb->notify_pending(sm->eapol_ctx); +} + + +/** + * eap_invalidate_cached_session - Mark cached session data invalid + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + */ +void eap_invalidate_cached_session(struct eap_sm *sm) +{ + if (sm) + eap_deinit_prev_method(sm, "invalidate"); +} + + +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) + return 0; /* Not using PBC */ + + return 1; +} + + +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) + return 0; /* Not using PIN */ + + return 1; +} diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h new file mode 100644 index 0000000000000..d7a562812bb36 --- /dev/null +++ b/src/eap_peer/eap.h @@ -0,0 +1,291 @@ +/* + * EAP peer state machine functions (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "defs.h" +#include "eap_common/eap_defs.h" +#include "eap_peer/eap_methods.h" + +struct eap_sm; +struct wpa_config_blob; +struct wpabuf; + +struct eap_method_type { + int vendor; + u32 method; +}; + +#ifdef IEEE8021X_EAPOL + +/** + * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_bool_var { + /** + * EAPOL_eapSuccess - EAP SUCCESS state reached + * + * EAP state machine reads and writes this value. + */ + EAPOL_eapSuccess, + + /** + * EAPOL_eapRestart - Lower layer request to restart authentication + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapRestart, + + /** + * EAPOL_eapFail - EAP FAILURE state reached + * + * EAP state machine writes this value. + */ + EAPOL_eapFail, + + /** + * EAPOL_eapResp - Response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapResp, + + /** + * EAPOL_eapNoResp - Request has been process; no response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapNoResp, + + /** + * EAPOL_eapReq - EAP request available from lower layer + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapReq, + + /** + * EAPOL_portEnabled - Lower layer is ready for communication + * + * EAP state machines reads this value. + */ + EAPOL_portEnabled, + + /** + * EAPOL_altAccept - Alternate indication of success (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altAccept, + + /** + * EAPOL_altReject - Alternate indication of failure (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altReject +}; + +/** + * enum eapol_int_var - EAPOL integer state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_int_var { + /** + * EAPOL_idleWhile - Outside time for EAP peer timeout + * + * This integer variable is used to provide an outside timer that the + * external (to EAP state machine) code must decrement by one every + * second until the value reaches zero. This is used in the same way as + * EAPOL state machine timers. EAP state machine reads and writes this + * value. + */ + EAPOL_idleWhile +}; + +/** + * struct eapol_callbacks - Callback functions from EAP to lower layer + * + * This structure defines the callback functions that EAP state machine + * requires from the lower layer (usually EAPOL state machine) for updating + * state variables and requesting information. eapol_ctx from + * eap_peer_sm_init() call will be used as the ctx parameter for these + * callback functions. + */ +struct eapol_callbacks { + /** + * get_config - Get pointer to the current network configuration + * @ctx: eapol_ctx from eap_peer_sm_init() call + */ + struct eap_peer_config * (*get_config)(void *ctx); + + /** + * get_bool - Get a boolean EAPOL state variable + * @variable: EAPOL boolean variable to get + * Returns: Value of the EAPOL variable + */ + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + + /** + * set_bool - Set a boolean EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL boolean variable to set + * @value: Value for the EAPOL variable + */ + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + + /** + * get_int - Get an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to get + * Returns: Value of the EAPOL variable + */ + unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + + /** + * set_int - Set an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to set + * @value: Value for the EAPOL variable + */ + void (*set_int)(void *ctx, enum eapol_int_var variable, + unsigned int value); + + /** + * get_eapReqData - Get EAP-Request data + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @len: Pointer to variable that will be set to eapReqDataLen + * Returns: Reference to eapReqData (EAP state machine will not free + * this) or %NULL if eapReqData not available. + */ + struct wpabuf * (*get_eapReqData)(void *ctx); + + /** + * set_config_blob - Set named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * notify_pending - Notify that a pending request can be retried + * @ctx: eapol_ctx from eap_peer_sm_init() call + * + * An EAP method can perform a pending operation (e.g., to get a + * response from an external process). Once the response is available, + * this callback function can be used to request EAPOL state machine to + * retry delivering the previously received (and still unanswered) EAP + * request to EAP state machine. + */ + void (*notify_pending)(void *ctx); + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @field: Field name (e.g., "IDENTITY") + * @txt: User readable text describing the required parameter + */ + void (*eap_param_needed)(void *ctx, const char *field, + const char *txt); +}; + +/** + * struct eap_config - Configuration for EAP state machine + */ +struct eap_config { + /** + * opensc_engine_path - OpenSC engine for OpenSSL engine support + * + * Usually, path to engine_opensc.so. + */ + const char *opensc_engine_path; + /** + * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support + * + * Usually, path to engine_pkcs11.so. + */ + const char *pkcs11_engine_path; + /** + * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine + * + * Usually, path to opensc-pkcs11.so. + */ + const char *pkcs11_module_path; + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; +}; + +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf); +void eap_peer_sm_deinit(struct eap_sm *sm); +int eap_peer_sm_step(struct eap_sm *sm); +void eap_sm_abort(struct eap_sm *sm); +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, + int verbose); +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted); +void eap_sm_request_identity(struct eap_sm *sm); +void eap_sm_request_password(struct eap_sm *sm); +void eap_sm_request_new_password(struct eap_sm *sm); +void eap_sm_request_pin(struct eap_sm *sm); +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len); +void eap_sm_request_passphrase(struct eap_sm *sm); +void eap_sm_notify_ctrl_attached(struct eap_sm *sm); +u32 eap_get_phase2_type(const char *name, int *vendor); +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count); +void eap_set_fast_reauth(struct eap_sm *sm, int enabled); +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +void eap_set_force_disabled(struct eap_sm *sm, int disabled); +int eap_key_available(struct eap_sm *sm); +void eap_notify_success(struct eap_sm *sm); +void eap_notify_lower_layer_success(struct eap_sm *sm); +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); +void eap_invalidate_cached_session(struct eap_sm *sm); + +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); + +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAP_H */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c new file mode 100644 index 0000000000000..f23714142586c --- /dev/null +++ b/src/eap_peer/eap_aka.c @@ -0,0 +1,1391 @@ +/* + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "pcsc_funcs.h" +#include "eap_common/eap_sim_common.h" +#include "sha1.h" +#include "sha256.h" +#include "crypto.h" +#include "eap_peer/eap_config.h" +#ifdef CONFIG_USIM_SIMULATOR +#include "hlr_auc_gw/milenage.h" +#endif /* CONFIG_USIM_SIMULATOR */ + + +struct eap_aka_data { + u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; + u8 auts[EAP_AKA_AUTS_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { + CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + } state; + + struct wpabuf *id_msgs; + int prev_id; + int result_ind, use_result_ind; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; + int kdf_negotiation; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_aka_state_txt(int state) +{ + switch (state) { + case CONTINUE: + return "CONTINUE"; + case RESULT_SUCCESS: + return "RESULT_SUCCESS"; + case RESULT_FAILURE: + return "RESULT_FAILURE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_aka_state(struct eap_aka_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", + eap_aka_state_txt(data->state), + eap_aka_state_txt(state)); + data->state = state; +} + + +static void * eap_aka_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + const char *phase1 = eap_get_config_phase1(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA; + + eap_aka_state(data, CONTINUE); + data->prev_id = -1; + + data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + + return data; +} + + +#ifdef EAP_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data = eap_aka_init(sm); + if (data == NULL) + return NULL; + data->eap_method = EAP_TYPE_AKA_PRIME; + return data; +} +#endif /* EAP_AKA_PRIME */ + + +static void eap_aka_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + if (data) { + os_free(data->pseudonym); + os_free(data->reauth_id); + os_free(data->last_eap_identity); + wpabuf_free(data->id_msgs); + os_free(data->network_name); + os_free(data); + } +} + + +static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) +{ + struct eap_peer_config *conf; + + wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); + + conf = eap_get_config(sm); + if (conf == NULL) + return -1; + if (conf->pcsc) { + return scard_umts_auth(sm->scard_ctx, data->rand, + data->autn, data->res, &data->res_len, + data->ik, data->ck, data->auts); + } + +#ifdef CONFIG_USIM_SIMULATOR + if (conf->password) { + u8 opc[16], k[16], sqn[6]; + const char *pos; + wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " + "implementation for UMTS authentication"); + if (conf->password_len < 78) { + wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " + "password"); + return -1; + } + pos = (const char *) conf->password; + if (hexstr2bin(pos, k, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, opc, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, sqn, 6)) + return -1; + + return milenage_check(opc, k, sqn, data->rand, data->autn, + data->ik, data->ck, + data->res, &data->res_len, data->auts); + } +#endif /* CONFIG_USIM_SIMULATOR */ + +#ifdef CONFIG_USIM_HARDCODED + wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " + "testing"); + + /* These hardcoded Kc and SRES values are used for testing. + * Could consider making them configurable. */ + os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); + data->res_len = EAP_AKA_RES_MAX_LEN; + os_memset(data->ik, '3', EAP_AKA_IK_LEN); + os_memset(data->ck, '4', EAP_AKA_CK_LEN); + { + u8 autn[EAP_AKA_AUTN_LEN]; + os_memset(autn, '1', EAP_AKA_AUTN_LEN); + if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " + "with expected value"); + return -1; + } + } +#if 0 + { + static int test_resync = 1; + if (test_resync) { + /* Test Resynchronization */ + test_resync = 0; + return -2; + } + } +#endif + return 0; + +#else /* CONFIG_USIM_HARDCODED */ + + wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " + "enabled"); + return -1; + +#endif /* CONFIG_USIM_HARDCODED */ +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + os_free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + os_free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + os_free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_aka_learn_ids(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + os_free(data->pseudonym); + data->pseudonym = os_malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next pseudonym"); + return -1; + } + os_memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + os_free(data->reauth_id); + data->reauth_id = os_malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next reauth_id"); + return -1; + } + os_memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static int eap_aka_add_id_msg(struct eap_aka_data *data, + const struct wpabuf *msg) +{ + if (msg == NULL) + return -1; + + if (data->id_msgs == NULL) { + data->id_msgs = wpabuf_dup(msg); + return data->id_msgs == NULL ? -1 : 0; + } + + if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) + return -1; + wpabuf_put_buf(data->id_msgs, msg); + + return 0; +} + + +static void eap_aka_add_checkcode(struct eap_aka_data *data, + struct eap_sim_msg *msg) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + + wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); + + if (data->id_msgs == NULL) { + /* + * No EAP-AKA/Identity packets were exchanged - send empty + * checkcode. + */ + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); + return; + } + + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else +#endif /* EAP_AKA_PRIME */ + sha1_vector(1, &addr, &len, hash); + + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); +} + + +static int eap_aka_verify_checkcode(struct eap_aka_data *data, + const u8 *checkcode, size_t checkcode_len) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + + if (checkcode == NULL) + return -1; + + if (data->id_msgs == NULL) { + if (checkcode_len != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " + "indicates that AKA/Identity messages were " + "used, but they were not"); + return -1; + } + return 0; + } + + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " + "indicates that AKA/Identity message were not " + "used, but they were"); + return -1; + } + + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else +#endif /* EAP_AKA_PRIME */ + sha1_vector(1, &addr, &len, hash); + + if (os_memcmp(hash, checkcode, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, + int err) +{ + struct eap_sim_msg *msg; + + eap_aka_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + eap_aka_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " + "(id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_synchronization_failure( + struct eap_aka_data *data, u8 id) +{ + struct eap_sim_msg *msg; + + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " + "(id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); + wpa_printf(MSG_DEBUG, " AT_AUTS"); + eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, + EAP_AKA_AUTS_LEN); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + enum eap_sim_id_req id_req) +{ + const u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ) { + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + eap_aka_clear_identities(data, CLEAR_PSEUDONYM | + CLEAR_REAUTH_ID); + } + } + if (id_req != NO_ID_REQ) + eap_aka_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_IDENTITY); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RES"); + eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, + data->res, data->res_len); + eap_aka_add_checkcode(data, msg); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, + u8 id, int counter_too_small, + const u8 *nonce_s) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", + id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + eap_aka_add_checkcode(data, msg); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, + u8 id, u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_NOTIFICATION); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + int id_error; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " + "used within one authentication"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + buf = eap_aka_response_identity(sm, data, id, attr->id_req); + + if (data->prev_id != id) { + eap_aka_add_id_msg(data, reqData); + eap_aka_add_id_msg(data, buf); + data->prev_id = id; + } + + return buf; +} + + +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); +} + + +#ifdef EAP_AKA_PRIME +static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, + u8 id, u16 kdf) +{ + struct eap_sim_msg *msg; + + data->kdf_negotiation = 1; + data->kdf = kdf; + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " + "select)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, + u8 id, struct eap_sim_attrs *attr) +{ + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_select(data, id, + EAP_AKA_PRIME_KDF); + } + + /* No matching KDF found - fail authentication as if AUTN had been + * incorrect */ + return eap_aka_authentication_reject(data, id); +} + + +static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + size_t i, j; + + if (attr->kdf_count == 0) + return 0; + + /* The only allowed (and required) duplication of a KDF is the addition + * of the selected KDF into the beginning of the list. */ + + if (data->kdf_negotiation) { + if (attr->kdf[0] != data->kdf) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "accept the selected KDF"); + return 0; + } + + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] == data->kdf) + break; + } + if (i == attr->kdf_count && + attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "duplicate the selected KDF"); + return 0; + } + + /* TODO: should check that the list is identical to the one + * used in the previous Challenge message apart from the added + * entry in the beginning. */ + } + + for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { + for (j = i + 1; j < attr->kdf_count; j++) { + if (attr->kdf[i] == attr->kdf[j]) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server " + "included a duplicated KDF"); + return 0; + } + } + } + + return 1; +} +#endif /* EAP_AKA_PRIME */ + + +static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + const u8 *identity; + size_t identity_len; + int res; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (!attr->kdf_input || attr->kdf_input_len == 0) { + wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " + "did not include non-empty AT_KDF_INPUT"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + os_free(data->network_name); + data->network_name = os_malloc(attr->kdf_input_len); + if (data->network_name == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " + "storing Network Name"); + return eap_aka_authentication_reject(data, id); + } + os_memcpy(data->network_name, attr->kdf_input, + attr->kdf_input_len); + data->network_name_len = attr->kdf_input_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " + "(AT_KDF_INPUT)", + data->network_name, data->network_name_len); + /* TODO: check Network Name per 3GPP.33.402 */ + + if (!eap_aka_prime_kdf_valid(data, attr)) + return eap_aka_authentication_reject(data, id); + + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_neg(data, id, attr); + + data->kdf = EAP_AKA_PRIME_KDF; + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + } + + if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { + u16 flags = WPA_GET_BE16(attr->bidding); + if ((flags & EAP_AKA_BIDDING_FLAG_D) && + eap_allowed_method(sm, EAP_VENDOR_IETF, + EAP_TYPE_AKA_PRIME)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " + "AKA' to AKA detected"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + } +#endif /* EAP_AKA_PRIME */ + + data->reauth = 0; + if (!attr->mac || !attr->rand || !attr->autn) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include%s%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : "", + !attr->autn ? " AT_AUTN" : ""); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); + os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); + + res = eap_aka_umts_auth(sm, data); + if (res == -1) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN)"); + return eap_aka_authentication_reject(data, id); + } else if (res == -2) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN seq# -> AUTS)"); + return eap_aka_synchronization_failure(data, id); + } else if (res) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^ AK for CK',IK' derivation */ + u16 amf = WPA_GET_BE16(data->autn + 6); + if (!(amf & 0x8000)) { + wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " + "not set (AMF=0x%4x)", amf); + return eap_aka_authentication_reject(data, id); + } + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_AKA_PRIME */ + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else + identity = eap_get_config_identity(sm, &identity_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " + "derivation", identity, identity_len); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, + data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "used invalid AT_MAC"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + u8 *decrypted; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + eap_aka_learn_ids(data, &eattr); + os_free(decrypted); + } + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_aka_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + /* RFC 4187 specifies that counter is initialized to one after + * fullauth, but initializing it to zero makes it easier to implement + * reauth verification. */ + data->counter = 0; + return eap_aka_response_challenge(data, id); +} + + +static int eap_aka_process_notification_reauth(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " + "message does not match with counter in reauth " + "message"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + return 0; +} + + +static int eap_aka_process_notification_auth(struct eap_aka_data *data, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_aka_process_notification_reauth(data, attr)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_process_notification( + struct eap_sm *sm, struct eap_aka_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-AKA: too many notification " + "rounds (only one allowed)"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " + "Notification message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_aka_process_notification_auth(data, reqData, attr)) { + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); + if (attr->notification >= 0 && attr->notification < 32768) { + eap_aka_state(data, FAILURE); + } else if (attr->notification == EAP_SIM_SUCCESS && + data->state == RESULT_SUCCESS) + eap_aka_state(data, SUCCESS); + return eap_aka_response_notification(data, id, attr->notification); +} + + +static struct wpabuf * eap_aka_process_reauthentication( + struct eap_sm *sm, struct eap_aka_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "did not have valid AT_MAC"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + os_free(decrypted); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { + struct wpabuf *res; + wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + os_free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + + res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); + os_free(decrypted); + + return res; + } + data->counter = eattr.counter; + + os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + data->reauth_id, + data->reauth_id_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys_reauth(data->counter, data->reauth_id, + data->reauth_id_len, + data->nonce_s, data->mk, + data->msk, data->emsk); + } + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_learn_ids(data, &eattr); + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_aka_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " + "fast reauths performed - force fullauth"); + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + os_free(decrypted); + return eap_aka_response_reauth(data, id, 0, data->nonce_s); +} + + +static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_aka_data *data = priv; + const struct eap_hdr *req; + u8 subtype, id; + struct wpabuf *res; + const u8 *pos; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); + eap_sm_request_identity(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, + &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + req = wpabuf_head(reqData); + id = req->identifier; + len = be_to_host16(req->length); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, + 0)) { + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_AKA_SUBTYPE_IDENTITY: + res = eap_aka_process_identity(sm, data, id, reqData, &attr); + break; + case EAP_AKA_SUBTYPE_CHALLENGE: + res = eap_aka_process_challenge(sm, data, id, reqData, &attr); + break; + case EAP_AKA_SUBTYPE_NOTIFICATION: + res = eap_aka_process_notification(sm, data, id, reqData, + &attr); + break; + case EAP_AKA_SUBTYPE_REAUTHENTICATION: + res = eap_aka_process_reauthentication(sm, data, id, reqData, + &attr); + break; + case EAP_AKA_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = data->use_result_ind ? + DECISION_UNCOND_SUCC : DECISION_COND_SUCC; + /* + * It is possible for the server to reply with AKA + * Notification, so we must allow the method to continue and + * not only accept EAP-Success at this point. + */ + ret->methodState = data->use_result_ind ? + METHOD_DONE : METHOD_MAY_CONT; + } else if (data->state == RESULT_FAILURE) + ret->methodState = METHOD_CONT; + else if (data->state == RESULT_SUCCESS) + ret->methodState = METHOD_CONT; + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + eap_aka_clear_identities(data, CLEAR_EAP_ID); + data->prev_id = -1; + wpabuf_free(data->id_msgs); + data->id_msgs = NULL; + data->use_result_ind = 0; + data->kdf_negotiation = 0; +} + + +static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + data->num_id_req = 0; + data->num_notification = 0; + eap_aka_state(data, CONTINUE); + return priv; +} + + +static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_aka_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_aka_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_init; + eap->deinit = eap_aka_deinit; + eap->process = eap_aka_process; + eap->isKeyAvailable = eap_aka_isKeyAvailable; + eap->getKey = eap_aka_getKey; + eap->has_reauth_data = eap_aka_has_reauth_data; + eap->deinit_for_reauth = eap_aka_deinit_for_reauth; + eap->init_for_reauth = eap_aka_init_for_reauth; + eap->get_identity = eap_aka_get_identity; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} + + +#ifdef EAP_AKA_PRIME +int eap_peer_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->deinit = eap_aka_deinit; + eap->process = eap_aka_process; + eap->isKeyAvailable = eap_aka_isKeyAvailable; + eap->getKey = eap_aka_getKey; + eap->has_reauth_data = eap_aka_has_reauth_data; + eap->deinit_for_reauth = eap_aka_deinit_for_reauth; + eap->init_for_reauth = eap_aka_init_for_reauth; + eap->get_identity = eap_aka_get_identity; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + + return ret; +} +#endif /* EAP_AKA_PRIME */ diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h new file mode 100644 index 0000000000000..94245c3d03dd1 --- /dev/null +++ b/src/eap_peer/eap_config.h @@ -0,0 +1,660 @@ +/* + * EAP peer configuration data + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_CONFIG_H +#define EAP_CONFIG_H + +/** + * struct eap_peer_config - EAP peer configuration/credentials + */ +struct eap_peer_config { + /** + * identity - EAP Identity + * + * This field is used to set the real user identity or NAI (for + * EAP-PSK/PAX/SAKE/GPSK). + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ + size_t identity_len; + + /** + * anonymous_identity - Anonymous EAP Identity + * + * This field is used for unencrypted use with EAP types that support + * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the + * real identity (identity field) only to the authentication server. + * + * If not set, the identity field will be used for both unencrypted and + * protected fields. + */ + u8 *anonymous_identity; + + /** + * anonymous_identity_len - Length of anonymous_identity + */ + size_t anonymous_identity_len; + + /** + * password - Password string for EAP + * + * This field can include either the plaintext password (default + * option) or a NtPasswordHash (16-byte MD4 hash of the unicode + * presentation of the password) if flags field has + * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can + * only be used with authentication mechanism that use this hash as the + * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, + * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). + * + * In addition, this field is used to configure a pre-shared key for + * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK + * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length + * PSK. + */ + u8 *password; + + /** + * password_len - Length of password field + */ + size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + * + * On Windows, trusted CA certificates can be loaded from the system + * certificate store by setting this to cert_store://name, e.g., + * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + */ + u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ + u8 *private_key_passwd; + + /** + * dh_file - File path to DH/DSA parameters file (in PEM format) + * + * This is an optional configuration file for setting parameters for an + * ephemeral DH key exchange. In most cases, the default RSA + * authentication does not use this configuration. However, it is + * possible setup RSA to use ephemeral DH key exchange. In addition, + * ciphers with DSA keys always use ephemeral DH keys. This can be used + * to achieve forward secrecy. If the file is in DSA parameters format, + * it will be automatically converted into DH params. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *dh_file; + + /** + * subject_match - Constraint for server certificate subject + * + * This substring is matched against the subject of the authentication + * server certificate. If this string is set, the server sertificate is + * only accepted if it contains this string in the subject. The subject + * string is in following format: + * + * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com + */ + u8 *subject_match; + + /** + * altsubject_match - Constraint for server certificate alt. subject + * + * Semicolon separated string of entries to be matched against the + * alternative subject name of the authentication server certificate. + * If this string is set, the server sertificate is only accepted if it + * contains one of the entries in an alternative subject name + * extension. + * + * altSubjectName string is in following format: TYPE:VALUE + * + * Example: EMAIL:server@example.com + * Example: DNS:server.example.com;DNS:server2.example.com + * + * Following types are supported: EMAIL, DNS, URI + */ + u8 *altsubject_match; + + /** + * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) + * + * This file can have one or more trusted CA certificates. If ca_cert2 + * and ca_path2 are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured. Full path to the file should be used since + * working directory may change when wpa_supplicant is run in the + * background. + * + * This field is like ca_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *ca_cert2; + + /** + * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + * + * This field is like ca_path, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *ca_path2; + + /** + * client_cert2 - File path to client certificate file + * + * This field is like client_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert2; + + /** + * private_key2 - File path to client private key file + * + * This field is like private_key, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key2; + + /** + * private_key2_passwd - Password for private key file + * + * This field is like private_key_passwd, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *private_key2_passwd; + + /** + * dh_file2 - File path to DH/DSA parameters file (in PEM format) + * + * This field is like dh_file, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *dh_file2; + + /** + * subject_match2 - Constraint for server certificate subject + * + * This field is like subject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *subject_match2; + + /** + * altsubject_match2 - Constraint for server certificate alt. subject + * + * This field is like altsubject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *altsubject_match2; + + /** + * eap_methods - Allowed EAP methods + * + * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of + * allowed EAP methods or %NULL if all methods are accepted. + */ + struct eap_method_type *eap_methods; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * String with field-value pairs, e.g., "peapver=0" or + * "peapver=1 peaplabel=1". + * + * 'peapver' can be used to force which PEAP version (0 or 1) is used. + * + * 'peaplabel=1' can be used to force new label, "client PEAP + * encryption", to be used during key derivation when PEAPv1 or newer. + * + * Most existing PEAPv1 implementation seem to be using the old label, + * "client EAP encryption", and wpa_supplicant is now using that as the + * default value. + * + * Some servers, e.g., Radiator, may require peaplabel=1 configuration + * to interoperate with PEAPv1; see eap_testing.txt for more details. + * + * 'peap_outer_success=0' can be used to terminate PEAP authentication + * on tunneled EAP-Success. This is required with some RADIUS servers + * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., + * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode). + * + * include_tls_length=1 can be used to force wpa_supplicant to include + * TLS Message Length field in all TLS messages even if they are not + * fragmented. + * + * sim_min_num_chal=3 can be used to configure EAP-SIM to require three + * challenges (by default, it accepts 2 or 3). + * + * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use + * protected result indication. + * + * fast_provisioning option can be used to enable in-line provisioning + * of EAP-FAST credentials (PAC): + * 0 = disabled, + * 1 = allow unauthenticated provisioning, + * 2 = allow authenticated provisioning, + * 3 = allow both unauthenticated and authenticated provisioning + * + * fast_max_pac_list_len=num option can be used to set the maximum + * number of PAC entries to store in a PAC list (default: 10). + * + * fast_pac_format=binary option can be used to select binary format + * for storing PAC entries in order to save some space (the default + * text format uses about 2.5 times the size of minimal binary format). + * + * crypto_binding option can be used to control PEAPv0 cryptobinding + * behavior: + * 0 = do not use cryptobinding (default) + * 1 = use cryptobinding if server supports it + * 2 = require cryptobinding + * + * EAP-WSC (WPS) uses following options: pin=Device_Password and + * uuid=Device_UUID + */ + char *phase1; + + /** + * phase2 - Phase2 (inner authentication with TLS tunnel) parameters + * + * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or + * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. + */ + char *phase2; + + /** + * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM + * + * This field is used to configure PC/SC smartcard interface. + * Currently, the only configuration is whether this field is %NULL (do + * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC. + * + * This field is used for EAP-SIM and EAP-AKA. + */ + char *pcsc; + + /** + * pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ + char *pin; + + /** + * engine - Enable OpenSSL engine (e.g., for smartcard access) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + int engine; + + /** + * engine_id - Engine ID for OpenSSL engine + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *engine_id; + + /** + * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + int engine2; + + + /** + * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2) + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * This field is like pin2, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * If left out, this will be asked through control interface. + */ + char *pin2; + + /** + * engine2_id - Engine ID for OpenSSL engine (Phase 2) + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine_id, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *engine2_id; + + + /** + * key_id - Key ID for OpenSSL engine + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key_id; + + /** + * cert_id - Cert ID for OpenSSL engine + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert_id; + + /** + * ca_cert_id - CA Cert ID for OpenSSL engine + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert_id; + + /** + * key2_id - Key ID for OpenSSL engine (phase2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key2_id; + + /** + * cert2_id - Cert ID for OpenSSL engine (phase2) + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert2_id; + + /** + * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2) + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert2_id; + + /** + * otp - One-time-password + * + * This field should not be set in configuration step. It is only used + * internally when OTP is entered through the control interface. + */ + u8 *otp; + + /** + * otp_len - Length of the otp field + */ + size_t otp_len; + + /** + * pending_req_identity - Whether there is a pending identity request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_identity; + + /** + * pending_req_password - Whether there is a pending password request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_password; + + /** + * pending_req_pin - Whether there is a pending PIN request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_pin; + + /** + * pending_req_new_password - Pending password update request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_new_password; + + /** + * pending_req_passphrase - Pending passphrase request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_passphrase; + + /** + * pending_req_otp - Whether there is a pending OTP request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + char *pending_req_otp; + + /** + * pending_req_otp_len - Length of the pending OTP request + */ + size_t pending_req_otp_len; + + /** + * pac_file - File path or blob name for the PAC entries (EAP-FAST) + * + * wpa_supplicant will need to be able to create this file and write + * updates to it when PAC is being provisioned or refreshed. Full path + * to the file should be used since working directory may change when + * wpa_supplicant is run in the background. + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *pac_file; + + /** + * mschapv2_retry - MSCHAPv2 retry in progress + * + * This field is used internally by EAP-MSCHAPv2 and should not be set + * as part of configuration. + */ + int mschapv2_retry; + + /** + * new_password - New password for password update + * + * This field is used during MSCHAPv2 password update. This is normally + * requested from the user through the control interface and not set + * from configuration. + */ + u8 *new_password; + + /** + * new_password_len - Length of new_password field + */ + size_t new_password_len; + + /** + * fragment_size - Maximum EAP fragment size in bytes (default 1398) + * + * This value limits the fragment size for EAP methods that support + * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set + * small enough to make the EAP messages fit in MTU of the network + * interface used for EAPOL. The default value is suitable for most + * cases. + */ + int fragment_size; + +#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) + /** + * flags - Network configuration flags (bitfield) + * + * This variable is used for internal flags to describe further details + * for the network parameters. + * bit 0 = password is represented as a 16-byte NtPasswordHash value + * instead of plaintext password + */ + u32 flags; +}; + + +/** + * struct wpa_config_blob - Named configuration blob + * + * This data structure is used to provide storage for binary objects to store + * abstract information like certificates and private keys inlined with the + * configuration data. + */ +struct wpa_config_blob { + /** + * name - Blob name + */ + char *name; + + /** + * data - Pointer to binary data + */ + u8 *data; + + /** + * len - Length of binary data + */ + size_t len; + + /** + * next - Pointer to next blob in the configuration + */ + struct wpa_config_blob *next; +}; + +#endif /* EAP_CONFIG_H */ diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c new file mode 100644 index 0000000000000..07e345f1f2db9 --- /dev/null +++ b/src/eap_peer/eap_fast.c @@ -0,0 +1,1715 @@ +/* + * EAP peer method: EAP-FAST (RFC 4851) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "tls.h" +#include "eap_common/eap_tlv_common.h" +#include "sha1.h" +#include "eap_fast_pac.h" + +#ifdef EAP_FAST_DYNAMIC +#include "eap_fast_pac.c" +#endif /* EAP_FAST_DYNAMIC */ + +/* TODO: + * - test session resumption and enable it if it interoperates + * - password change (pending mschapv2 packet; replay decrypted packet) + */ + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv); + + +struct eap_fast_data { + struct eap_ssl_data ssl; + + int fast_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + int resuming; /* starting a resumed session */ + struct eap_fast_key_block_provisioning *key_block_p; +#define EAP_FAST_PROV_UNAUTH 1 +#define EAP_FAST_PROV_AUTH 2 + int provisioning_allowed; /* Allowed PAC provisioning modes */ + int provisioning; /* doing PAC provisioning (not the normal auth) */ + int anon_provisioning; /* doing anonymous (unauthenticated) + * provisioning */ + int session_ticket_used; + + u8 key_data[EAP_FAST_KEY_LEN]; + u8 emsk[EAP_EMSK_LEN]; + int success; + + struct eap_fast_pac *pac; + struct eap_fast_pac *current_pac; + size_t max_pac_list_len; + int use_pac_binary_format; + + u8 simck[EAP_FAST_SIMCK_LEN]; + int simck_idx; + + struct wpabuf *pending_phase2_req; +}; + + +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_fast_data *data = ctx; + + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); + + if (client_random == NULL || server_random == NULL || + master_secret == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall " + "back to full TLS handshake"); + data->session_ticket_used = 0; + if (data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a " + "new PAC-Key"); + data->provisioning = 1; + data->current_pac = NULL; + } + return 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len); + + if (data->current_pac == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for " + "using SessionTicket"); + data->session_ticket_used = 0; + return 0; + } + + eap_fast_derive_master_secret(data->current_pac->pac_key, + server_random, client_random, + master_secret); + + data->session_ticket_used = 1; + + return 1; +} + + +static int eap_fast_parse_phase1(struct eap_fast_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "fast_provisioning="); + if (pos) { + data->provisioning_allowed = atoi(pos + 18); + wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning " + "mode: %d", data->provisioning_allowed); + } + + pos = os_strstr(phase1, "fast_max_pac_list_len="); + if (pos) { + data->max_pac_list_len = atoi(pos + 22); + if (data->max_pac_list_len == 0) + data->max_pac_list_len = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu", + (unsigned long) data->max_pac_list_len); + } + + pos = os_strstr(phase1, "fast_pac_format=binary"); + if (pos) { + data->use_pac_binary_format = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " + "list"); + } + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->fast_version = EAP_FAST_VERSION; + data->max_pac_list_len = 10; + + if (config && config->phase1 && + eap_fast_parse_phase1(data, config->phase1) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_deinit(sm, data); + return NULL; + } + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_fast_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " + "callback"); + eap_fast_deinit(sm, data); + return NULL; + } + + /* + * The local RADIUS server in a Cisco AP does not seem to like empty + * fragments before data, so disable that workaround for CBC. + * TODO: consider making this configurable + */ + if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS " + "workarounds"); + } + + if (data->use_pac_binary_format && + eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (!data->use_pac_binary_format && + eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); + + if (data->pac == NULL && !data->provisioning_allowed) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and " + "provisioning disabled"); + eap_fast_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + struct eap_fast_pac *pac, *prev; + + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + os_free(data->phase2_types); + os_free(data->key_block_p); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + + pac = data->pac; + prev = NULL; + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + } + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +static int eap_fast_derive_msk(struct eap_fast_data *data) +{ + eap_fast_derive_eap_msk(data->simck, data->key_data); + eap_fast_derive_eap_emsk(data->simck, data->emsk); + data->success = 1; + return 0; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 *sks; + + /* RFC 4851, Section 5.1: + * Extra key material after TLS key_block: session_key_seed[40] + */ + + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + EAP_FAST_SKS_LEN); + if (sks == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " + "session_key_seed"); + return; + } + + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + sks, EAP_FAST_SKS_LEN); + data->simck_idx = 0; + os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); + os_free(sks); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + os_free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, + "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + data->simck_idx = 0; + os_memcpy(data->simck, data->key_block_p->session_key_seed, + EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +{ + if (data->anon_provisioning) + eap_fast_derive_key_provisioning(sm, data); + else + eap_fast_derive_key_auth(sm, data); +} + + +static int eap_fast_init_phase2_method(struct eap_sm *sm, + struct eap_fast_data *data) +{ + data->phase2_method = + eap_peer_get_eap_method(data->phase2_type.vendor, + data->phase2_type.method); + if (data->phase2_method == NULL) + return -1; + + if (data->key_block_p) { + sm->auth_challenge = data->key_block_p->server_challenge; + sm->peer_challenge = data->key_block_p->client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + + return data->phase2_priv == NULL ? -1 : 0; +} + + +static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type) +{ + size_t i; + + /* TODO: TNC with anonymous provisioning; need to require both + * completed MSCHAPv2 and TNC */ + + if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) { + wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed " + "during unauthenticated provisioning; reject phase2" + " type %d", type); + return -1; + } + +#ifdef EAP_TNC + if (type == EAP_TYPE_TNC) { + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_TNC; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " + "vendor %d method %d for TNC", + data->phase2_type.vendor, + data->phase2_type.method); + return 0; + } +#endif /* EAP_TNC */ + + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_types[i].method != type) + continue; + + data->phase2_type.vendor = data->phase2_types[i].vendor; + data->phase2_type.method = data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " + "vendor %d method %d", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + + if (type != data->phase2_type.method || type == EAP_TYPE_NONE) + return -1; + + return 0; +} + + +static int eap_fast_phase2_request(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf msg; + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos); + if (*pos == EAP_TYPE_IDENTITY) { + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + return 0; + } + + if (data->phase2_priv && data->phase2_method && + *pos != data->phase2_type.method) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - " + "deinitialize previous method"); + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + } + + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE && + eap_fast_select_phase2_method(data, *pos) < 0) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL && + eap_fast_init_phase2_method(sm, data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if (*resp == NULL || + (iret.methodState == METHOD_DONE && + iret.decision == DECISION_FAIL)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + + if (*resp == NULL && config && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } else if (*resp == NULL) + return -1; + + return 0; +} + + +static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type) +{ + struct wpabuf *buf; + struct eap_tlv_nak_tlv *nak; + buf = wpabuf_alloc(sizeof(*nak)); + if (buf == NULL) + return NULL; + nak = wpabuf_put(buf, sizeof(*nak)); + nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV); + nak->length = host_to_be16(6); + nak->vendor_id = host_to_be32(vendor_id); + nak->nak_type = host_to_be16(tlv_type); + return buf; +} + + +static struct wpabuf * eap_fast_tlv_result(int status, int intermediate) +{ + struct wpabuf *buf; + struct eap_tlv_intermediate_result_tlv *result; + buf = wpabuf_alloc(sizeof(*result)); + if (buf == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)", + intermediate ? "Intermediate " : "", status); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (intermediate ? + EAP_TLV_INTERMEDIATE_RESULT_TLV : + EAP_TLV_RESULT_TLV)); + result->length = host_to_be16(2); + result->status = host_to_be16(status); + return buf; +} + + +static struct wpabuf * eap_fast_tlv_pac_ack(void) +{ + struct wpabuf *buf; + struct eap_tlv_result_tlv *res; + struct eap_tlv_pac_ack_tlv *ack; + + buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); + if (buf == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)"); + ack = wpabuf_put(buf, sizeof(*ack)); + ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV | + EAP_TLV_TYPE_MANDATORY); + ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr)); + ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); + ack->pac_len = host_to_be16(2); + ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + return buf; +} + + +static struct wpabuf * eap_fast_process_eap_payload_tlv( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, const struct eap_hdr *req, + u8 *eap_payload_tlv, size_t eap_payload_tlv_len) +{ + struct eap_hdr *hdr; + struct wpabuf *resp = NULL; + + if (eap_payload_tlv_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP " + "Payload TLV (len=%lu)", + (unsigned long) eap_payload_tlv_len); + return NULL; + } + + hdr = (struct eap_hdr *) eap_payload_tlv; + if (be_to_host16(hdr->length) > eap_payload_tlv_len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in " + "EAP Payload TLV"); + return NULL; + } + + if (hdr->code != EAP_CODE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + return NULL; + } + + if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing " + "failed"); + return NULL; + } + + return eap_fast_tlv_eap_payload(resp); +} + + +static int eap_fast_validate_crypto_binding( + struct eap_tlv_crypto_binding_tlv *_bind) +{ + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + _bind->version, _bind->received_version, _bind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + _bind->nonce, sizeof(_bind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + _bind->compound_mac, sizeof(_bind->compound_mac)); + + if (_bind->version != EAP_FAST_VERSION || + _bind->received_version != EAP_FAST_VERSION || + _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in " + "Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + _bind->version, _bind->received_version, + _bind->subtype); + return -1; + } + + return 0; +} + + +static void eap_fast_write_crypto_binding( + struct eap_tlv_crypto_binding_tlv *rbind, + struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk) +{ + rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV); + rbind->length = host_to_be16(sizeof(*rbind) - + sizeof(struct eap_tlv_hdr)); + rbind->version = EAP_FAST_VERSION; + rbind->received_version = _bind->version; + rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; + os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce)); + inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); + hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind), + rbind->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + rbind->version, rbind->received_version, rbind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + rbind->nonce, sizeof(rbind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + rbind->compound_mac, sizeof(rbind->compound_mac)); +} + + +static int eap_fast_get_phase2_key(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return -1; + } + + if (data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + if (key_len == 32 && + data->phase2_method->vendor == EAP_VENDOR_IETF && + data->phase2_method->method == EAP_TYPE_MSCHAPV2) { + /* + * EAP-FAST uses reverse order for MS-MPPE keys when deriving + * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct + * ISK for EAP-FAST cryptobinding. + */ + os_memcpy(isk, key + 16, 16); + os_memcpy(isk + 16, key, 16); + } else + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data, + u8 *cmk) +{ + u8 isk[32], imck[60]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC " + "calculation", data->simck_idx + 1); + + /* + * RFC 4851, Section 5.2: + * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", + * MSK[j], 60) + * S-IMCK[j] = first 40 octets of IMCK[j] + * CMK[j] = last 20 octets of IMCK[j] + */ + + if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); + sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + data->simck_idx++; + os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", + data->simck, EAP_FAST_SIMCK_LEN); + os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", + cmk, EAP_FAST_CMK_LEN); + + return 0; +} + + +static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type) +{ + struct eap_tlv_hdr *pac; + struct eap_tlv_request_action_tlv *act; + struct eap_tlv_pac_type_tlv *type; + + act = (struct eap_tlv_request_action_tlv *) pos; + act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV); + act->length = host_to_be16(2); + act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV); + + pac = (struct eap_tlv_hdr *) (act + 1); + pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV); + pac->length = host_to_be16(sizeof(*type)); + + type = (struct eap_tlv_pac_type_tlv *) (pac + 1); + type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE); + type->length = host_to_be16(2); + type->pac_type = host_to_be16(pac_type); + + return (u8 *) (type + 1); +} + + +static struct wpabuf * eap_fast_process_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len) +{ + struct wpabuf *resp; + u8 *pos; + u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN]; + int res; + size_t len; + + if (eap_fast_validate_crypto_binding(_bind) < 0) + return NULL; + + if (eap_fast_get_cmk(sm, data, cmk) < 0) + return NULL; + + /* Validate received Compound MAC */ + os_memcpy(cmac, _bind->compound_mac, sizeof(cmac)); + os_memset(_bind->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound " + "MAC calculation", (u8 *) _bind, bind_len); + hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len, + _bind->compound_mac); + res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", + cmac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", + _bind->compound_mac, sizeof(cmac)); + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); + os_memcpy(_bind->compound_mac, cmac, sizeof(cmac)); + return NULL; + } + + /* + * Compound MAC was valid, so authentication succeeded. Reply with + * crypto binding to allow server to complete authentication. + */ + + len = sizeof(struct eap_tlv_crypto_binding_tlv); + resp = wpabuf_alloc(len); + if (resp == NULL) + return NULL; + + if (!data->anon_provisioning && data->phase2_success && + eap_fast_derive_msk(data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + data->phase2_success = 0; + wpabuf_free(resp); + return NULL; + } + + pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); + eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) + pos, _bind, cmk); + + return resp; +} + + +static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type, + u8 *pos, size_t len, int *pac_key_found) +{ + switch (type & 0x7fff) { + case PAC_TYPE_PAC_KEY: + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len); + if (len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key " + "length %lu", (unsigned long) len); + break; + } + *pac_key_found = 1; + os_memcpy(entry->pac_key, pos, len); + break; + case PAC_TYPE_PAC_OPAQUE: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len); + entry->pac_opaque = pos; + entry->pac_opaque_len = len; + break; + case PAC_TYPE_PAC_INFO: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len); + entry->pac_info = pos; + entry->pac_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d", + type); + break; + } +} + + +static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry, + u8 *pac, size_t pac_len) +{ + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type, pac_key_found = 0; + + pos = pac; + left = pac_len; + + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found); + + pos += len; + left -= len; + } + + if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include " + "all the required fields"); + return -1; + } + + return 0; +} + + +static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type, + u8 *pos, size_t len) +{ + u16 pac_type; + u32 lifetime; + struct os_time now; + + switch (type & 0x7fff) { + case PAC_TYPE_CRED_LIFETIME: + if (len != 4) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "Invalid CRED_LIFETIME length - ignored", + pos, len); + return 0; + } + + /* + * This is not currently saved separately in PAC files since + * the server can automatically initiate PAC update when + * needed. Anyway, the information is available from PAC-Info + * dump if it is needed for something in the future. + */ + lifetime = WPA_GET_BE32(pos); + os_get_time(&now); + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d " + "(%d days)", + lifetime, (lifetime - (u32) now.sec) / 86400); + break; + case PAC_TYPE_A_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID", + pos, len); + entry->a_id = pos; + entry->a_id_len = len; + break; + case PAC_TYPE_I_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID", + pos, len); + entry->i_id = pos; + entry->i_id_len = len; + break; + case PAC_TYPE_A_ID_INFO: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info", + pos, len); + entry->a_id_info = pos; + entry->a_id_info_len = len; + break; + case PAC_TYPE_PAC_TYPE: + /* + * draft-cam-winget-eap-fast-provisioning-04.txt, + * Section 4.2.6 - PAC-Type TLV + */ + if (len != 2) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type " + "length %lu (expected 2)", + (unsigned long) len); + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-FAST: PAC-Info - PAC-Type", + pos, len); + return -1; + } + pac_type = WPA_GET_BE16(pos); + if (pac_type != PAC_TYPE_TUNNEL_PAC && + pac_type != PAC_TYPE_USER_AUTHORIZATION && + pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type " + "%d", pac_type); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d", + pac_type); + entry->pac_type = pac_type; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info " + "type %d", type); + break; + } + + return 0; +} + + +static int eap_fast_process_pac_info(struct eap_fast_pac *entry) +{ + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type; + + /* draft-cam-winget-eap-fast-provisioning-04.txt, Section 4.2.4 */ + + /* PAC-Type defaults to Tunnel PAC (Type 1) */ + entry->pac_type = PAC_TYPE_TUNNEL_PAC; + + pos = entry->pac_info; + left = entry->pac_info_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + if (eap_fast_parse_pac_info(entry, type, pos, len) < 0) + return -1; + + pos += len; + left -= len; + } + + if (entry->a_id == NULL || entry->a_id_info == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include " + "all the required fields"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + u8 *pac, size_t pac_len) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct eap_fast_pac entry; + + os_memset(&entry, 0, sizeof(entry)); + if (eap_fast_process_pac_tlv(&entry, pac, pac_len) || + eap_fast_process_pac_info(&entry)) + return NULL; + + eap_fast_add_pac(&data->pac, &data->current_pac, &entry); + eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); + if (data->use_pac_binary_format) + eap_fast_save_pac_bin(sm, data->pac, config->pac_file); + else + eap_fast_save_pac(sm, data->pac, config->pac_file); + + if (data->provisioning) { + if (data->anon_provisioning) { + /* + * Unauthenticated provisioning does not provide keying + * material and must end with an EAP-Failure. + * Authentication will be done separately after this. + */ + data->success = 0; + ret->decision = DECISION_FAIL; + } else { + /* + * Server may or may not allow authenticated + * provisioning also for key generation. + */ + ret->decision = DECISION_COND_SUCC; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- Provisioning completed successfully"); + } else { + /* + * This is PAC refreshing, i.e., normal authentication that is + * expected to be completed with an EAP-Success. + */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- PAC refreshing completed successfully"); + ret->decision = DECISION_UNCOND_SUCC; + } + ret->methodState = METHOD_DONE; + return eap_fast_tlv_pac_ack(); +} + + +static int eap_fast_parse_decrypted(struct wpabuf *decrypted, + struct eap_fast_tlv_parse *tlv, + struct wpabuf **resp) +{ + int mandatory, tlv_type, len, res; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + /* Parse TLVs from the decrypted Phase 2 data */ + pos = wpabuf_mhead(decrypted); + end = pos + wpabuf_len(decrypted); + while (pos + 4 < end) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + *resp = eap_fast_tlv_nak(0, tlv_type); + break; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: ignored " + "unknown optional TLV type %d", + tlv_type); + } + } + + pos += len; + } + + return 0; +} + + +static int eap_fast_encrypt_response(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *resp, + u8 identifier, struct wpabuf **out_data) +{ + if (resp == NULL) + return 0; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data", + resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " + "frame"); + } + wpabuf_free(resp); + + return 0; +} + + +static struct wpabuf * eap_fast_pac_request(void) +{ + struct wpabuf *tmp; + u8 *pos, *pos2; + + tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) + + sizeof(struct eap_tlv_request_action_tlv) + + sizeof(struct eap_tlv_pac_type_tlv)); + if (tmp == NULL) + return NULL; + + pos = wpabuf_put(tmp, 0); + pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC); + wpabuf_put(tmp, pos2 - pos); + return tmp; +} + + +static int eap_fast_process_decrypted(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + struct wpabuf *decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL, *tmp; + struct eap_fast_tlv_parse tlv; + int failed = 0; + + if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0) + return 0; + if (resp) + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + + if (tlv.result == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + } + + if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + } + + if (tlv.crypto_binding) { + tmp = eap_fast_process_crypto_binding(sm, data, ret, + tlv.crypto_binding, + tlv.crypto_binding_len); + if (tmp == NULL) + failed = 1; + else + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) { + tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE : + EAP_TLV_RESULT_SUCCESS, 1); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.eap_payload_tlv) { + tmp = eap_fast_process_eap_payload_tlv( + sm, data, ret, req, tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " + "acknowledging success"); + failed = 1; + } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) { + tmp = eap_fast_process_pac(sm, data, ret, tlv.pac, + tlv.pac_len); + resp = wpabuf_concat(resp, tmp); + } + + if (data->current_pac == NULL && data->provisioning && + !data->anon_provisioning && !tlv.pac && + (tlv.iresult == EAP_TLV_RESULT_SUCCESS || + tlv.result == EAP_TLV_RESULT_SUCCESS)) { + /* + * Need to request Tunnel PAC when using authenticated + * provisioning. + */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC"); + tmp = eap_fast_pac_request(); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) { + tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0); + resp = wpabuf_concat(tmp, resp); + } else if (failed) { + tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); + resp = wpabuf_concat(tmp, resp); + } + + if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed && + tlv.crypto_binding && data->phase2_success) { + if (data->anon_provisioning) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated " + "provisioning completed successfully."); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " + "completed successfully."); + if (data->provisioning) + ret->methodState = METHOD_MAY_CONT; + else + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + } + + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " + "empty response packet"); + resp = wpabuf_alloc(1); + } + + return eap_fast_encrypt_response(sm, data, resp, req->identifier, + out_data); +} + + +static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted; + int res; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, + req->identifier, NULL, out_data); + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (res) + return res; + +continue_req: + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", + in_decrypted); + + if (wpabuf_len(in_decrypted) < 4) { + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "TLV frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return -1; + } + + res = eap_fast_process_decrypted(sm, data, ret, req, + in_decrypted, out_data); + + wpabuf_free(in_decrypted); + + return res; +} + + +static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) +{ + const u8 *a_id; + struct pac_tlv_hdr *hdr; + + /* + * Parse authority identity (A-ID) from the EAP-FAST/Start. This + * supports both raw A-ID and one inside an A-ID TLV. + */ + a_id = buf; + *id_len = len; + if (len > sizeof(*hdr)) { + int tlen; + hdr = (struct pac_tlv_hdr *) buf; + tlen = be_to_host16(hdr->len); + if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && + sizeof(*hdr) + tlen <= len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " + "(Start)"); + a_id = (u8 *) (hdr + 1); + *id_len = tlen; + } + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len); + + return a_id; +} + + +static void eap_fast_select_pac(struct eap_fast_data *data, + const u8 *a_id, size_t a_id_len) +{ + data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len, + PAC_TYPE_TUNNEL_PAC); + if (data->current_pac == NULL) { + /* + * Tunnel PAC was not available for this A-ID. Try to use + * Machine Authentication PAC, if one is available. + */ + data->current_pac = eap_fast_get_pac( + data->pac, a_id, a_id_len, + PAC_TYPE_MACHINE_AUTHENTICATION); + } + + if (data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID " + "(PAC-Type %d)", data->current_pac->pac_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info", + data->current_pac->a_id_info, + data->current_pac->a_id_info_len); + } +} + + +static int eap_fast_use_pac_opaque(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_fast_pac *pac) +{ + u8 *tlv; + size_t tlv_len, olen; + struct eap_tlv_hdr *ehdr; + + olen = pac->pac_opaque_len; + tlv_len = sizeof(*ehdr) + olen; + tlv = os_malloc(tlv_len); + if (tlv) { + ehdr = (struct eap_tlv_hdr *) tlv; + ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE); + ehdr->length = host_to_be16(olen); + os_memcpy(ehdr + 1, pac->pac_opaque, olen); + } + if (tlv == NULL || + tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, + tlv, tlv_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS " + "extension"); + os_free(tlv); + return -1; + } + os_free(tlv); + + return 0; +} + + +static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm, + struct eap_fast_data *data) +{ + if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque " + "TLS extension"); + return -1; + } + return 0; +} + + +static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 ciphers[5]; + int count = 0; + + if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated " + "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA; + } + + if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated " + "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA; + ciphers[count++] = TLS_CIPHER_AES128_SHA; + ciphers[count++] = TLS_CIPHER_RC4_SHA; + } + + ciphers[count++] = TLS_CIPHER_NONE; + + if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, + ciphers)) { + wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS " + "cipher suites for provisioning"); + return -1; + } + + return 0; +} + + +static int eap_fast_process_start(struct eap_sm *sm, + struct eap_fast_data *data, u8 flags, + const u8 *pos, size_t left) +{ + const u8 *a_id; + size_t a_id_len; + + /* EAP-FAST Version negotiation (section 3.1) */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)", + flags & EAP_PEAP_VERSION_MASK, data->fast_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version) + data->fast_version = flags & EAP_PEAP_VERSION_MASK; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", + data->fast_version); + + a_id = eap_fast_get_a_id(pos, left, &a_id_len); + eap_fast_select_pac(data, a_id, a_id_len); + + if (data->resuming && data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - " + "do not add PAC-Opaque to TLS ClientHello"); + if (eap_fast_clear_pac_opaque_ext(sm, data) < 0) + return -1; + } else if (data->current_pac) { + /* + * PAC found for the A-ID and we are not resuming an old + * session, so add PAC-Opaque extension to ClientHello. + */ + if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0) + return -1; + } else { + /* No PAC found, so we must provision one. */ + if (!data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and " + "provisioning disabled"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - " + "starting provisioning"); + if (eap_fast_set_provisioning_ciphers(sm, data) < 0 || + eap_fast_clear_pac_opaque_ext(sm, data) < 0) + return -1; + data->provisioning = 1; + } + + return 0; +} + + +static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_fast_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + if (eap_fast_process_start(sm, data, flags, pos, left) < 0) + return NULL; + + left = 0; /* A-ID is not used in further packet processing */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + /* Process tunneled (encrypted) phase 2 data. */ + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* + * Ack possible Alert that may have caused failure in + * decryption. + */ + res = 1; + } + } else { + /* Continue processing TLS handshake (phase 1). */ + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_FAST, + data->fast_version, id, pos, + left, &resp); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char cipher[80]; + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS done, proceed to Phase 2"); + if (data->provisioning && + (!(data->provisioning_allowed & + EAP_FAST_PROV_AUTH) || + tls_get_cipher(sm->ssl_ctx, data->ssl.conn, + cipher, sizeof(cipher)) < 0 || + os_strstr(cipher, "ADH-") || + os_strstr(cipher, "anon"))) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Using " + "anonymous (unauthenticated) " + "provisioning"); + data->anon_provisioning = 1; + } else + data->anon_provisioning = 0; + data->resuming = 0; + eap_fast_derive_keys(sm, data); + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_fast_decrypt(sm, data, ret, req, &msg, + &resp); + } + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, + data->fast_version); + } + + return resp; +} + + +#if 0 /* FIX */ +static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + os_free(data->key_block_p); + data->key_block_p = NULL; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +} + + +static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_success = 0; + data->resuming = 1; + data->provisioning = 0; + data->anon_provisioning = 0; + data->simck_idx = 0; + return priv; +} +#endif + + +static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_fast_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-FAST Phase2 method=%s\n", + data->phase2_method->name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->success; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = os_malloc(EAP_FAST_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_FAST_KEY_LEN; + os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN); + + return key; +} + + +static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_fast_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); + if (eap == NULL) + return -1; + + eap->init = eap_fast_init; + eap->deinit = eap_fast_deinit; + eap->process = eap_fast_process; + eap->isKeyAvailable = eap_fast_isKeyAvailable; + eap->getKey = eap_fast_getKey; + eap->get_status = eap_fast_get_status; +#if 0 + eap->has_reauth_data = eap_fast_has_reauth_data; + eap->deinit_for_reauth = eap_fast_deinit_for_reauth; + eap->init_for_reauth = eap_fast_init_for_reauth; +#endif + eap->get_emsk = eap_fast_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c new file mode 100644 index 0000000000000..77893d6195970 --- /dev/null +++ b/src/eap_peer/eap_fast_pac.c @@ -0,0 +1,921 @@ +/* + * EAP peer method: EAP-FAST PAC file processing + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_config.h" +#include "eap_i.h" +#include "eap_fast_pac.h" + +/* TODO: encrypt PAC-Key in the PAC file */ + + +/* Text data format */ +static const char *pac_file_hdr = + "wpa_supplicant EAP-FAST PAC file - version 1"; + +/* + * Binary data format + * 4-octet magic value: 6A E4 92 0C + * 2-octet version (big endian) + * <version specific data> + * + * version=0: + * Sequence of PAC entries: + * 2-octet PAC-Type (big endian) + * 32-octet PAC-Key + * 2-octet PAC-Opaque length (big endian) + * <variable len> PAC-Opaque data (length bytes) + * 2-octet PAC-Info length (big endian) + * <variable len> PAC-Info data (length bytes) + */ + +#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c +#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0 + + +/** + * eap_fast_free_pac - Free PAC data + * @pac: Pointer to the PAC entry + * + * Note that the PAC entry must not be in a list since this function does not + * remove the list links. + */ +void eap_fast_free_pac(struct eap_fast_pac *pac) +{ + os_free(pac->pac_opaque); + os_free(pac->pac_info); + os_free(pac->a_id); + os_free(pac->i_id); + os_free(pac->a_id_info); + os_free(pac); +} + + +/** + * eap_fast_get_pac - Get a PAC entry based on A-ID + * @pac_root: Pointer to root of the PAC list + * @a_id: A-ID to search for + * @a_id_len: Length of A-ID + * @pac_type: PAC-Type to search for + * Returns: Pointer to the PAC entry, or %NULL if A-ID not found + */ +struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type) +{ + struct eap_fast_pac *pac = pac_root; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + return pac; + } + pac = pac->next; + } + return NULL; +} + + +static void eap_fast_remove_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + const u8 *a_id, size_t a_id_len, u16 pac_type) +{ + struct eap_fast_pac *pac, *prev; + + pac = *pac_root; + prev = NULL; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + if (prev == NULL) + *pac_root = pac->next; + else + prev->next = pac->next; + if (*pac_current == pac) + *pac_current = NULL; + eap_fast_free_pac(pac); + break; + } + prev = pac; + pac = pac->next; + } +} + + +static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) +{ + if (src) { + *dst = os_malloc(src_len); + if (*dst == NULL) + return -1; + os_memcpy(*dst, src, src_len); + *dst_len = src_len; + } + return 0; +} + + +/** + * eap_fast_add_pac - Add a copy of a PAC entry to a list + * @pac_root: Pointer to PAC list root pointer + * @pac_current: Pointer to the current PAC pointer + * @entry: New entry to clone and add to the list + * Returns: 0 on success, -1 on failure + * + * This function makes a clone of the given PAC entry and adds this copied + * entry to the list (pac_root). If an old entry for the same A-ID is found, + * it will be removed from the PAC list and in this case, pac_current entry + * is set to %NULL if it was the removed entry. + */ +int eap_fast_add_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + struct eap_fast_pac *entry) +{ + struct eap_fast_pac *pac; + + if (entry == NULL || entry->a_id == NULL) + return -1; + + /* Remove a possible old entry for the matching A-ID. */ + eap_fast_remove_pac(pac_root, pac_current, + entry->a_id, entry->a_id_len, entry->pac_type); + + /* Allocate a new entry and add it to the list of PACs. */ + pac = os_zalloc(sizeof(*pac)); + if (pac == NULL) + return -1; + + pac->pac_type = entry->pac_type; + os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); + if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, + entry->pac_opaque, entry->pac_opaque_len) < 0 || + eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len, + entry->pac_info, entry->pac_info_len) < 0 || + eap_fast_copy_buf(&pac->a_id, &pac->a_id_len, + entry->a_id, entry->a_id_len) < 0 || + eap_fast_copy_buf(&pac->i_id, &pac->i_id_len, + entry->i_id, entry->i_id_len) < 0 || + eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len, + entry->a_id_info, entry->a_id_info_len) < 0) { + eap_fast_free_pac(pac); + return -1; + } + + pac->next = *pac_root; + *pac_root = pac; + + return 0; +} + + +struct eap_fast_read_ctx { + FILE *f; + const char *pos; + const char *end; + int line; + char *buf; + size_t buf_len; +}; + +static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value) +{ + char *pos; + + rc->line++; + if (rc->f) { + if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) + return -1; + } else { + const char *l_end; + size_t len; + if (rc->pos >= rc->end) + return -1; + l_end = rc->pos; + while (l_end < rc->end && *l_end != '\n') + l_end++; + len = l_end - rc->pos; + if (len >= rc->buf_len) + len = rc->buf_len - 1; + os_memcpy(rc->buf, rc->pos, len); + rc->buf[len] = '\0'; + rc->pos = l_end + 1; + } + + rc->buf[rc->buf_len - 1] = '\0'; + pos = rc->buf; + while (*pos != '\0') { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + pos = os_strchr(rc->buf, '='); + if (pos) + *pos++ = '\0'; + *value = pos; + + return 0; +} + + +static u8 * eap_fast_parse_hex(const char *value, size_t *len) +{ + int hlen; + u8 *buf; + + if (value == NULL) + return NULL; + hlen = os_strlen(value); + if (hlen & 1) + return NULL; + *len = hlen / 2; + buf = os_malloc(*len); + if (buf == NULL) + return NULL; + if (hexstr2bin(value, buf, *len)) { + os_free(buf); + return NULL; + } + return buf; +} + + +static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file, + struct eap_fast_read_ctx *rc) +{ + os_memset(rc, 0, sizeof(*rc)); + + rc->buf_len = 2048; + rc->buf = os_malloc(rc->buf_len); + if (rc->buf == NULL) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + const struct wpa_config_blob *blob; + blob = eap_get_config_blob(sm, pac_file + 7); + if (blob == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file + 7); + os_free(rc->buf); + return -1; + } + rc->pos = (char *) blob->data; + rc->end = (char *) blob->data + blob->len; + } else { + rc->f = fopen(pac_file, "rb"); + if (rc->f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file); + os_free(rc->buf); + return -1; + } + } + + return 0; +} + + +static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc) +{ + os_free(rc->buf); + if (rc->f) + fclose(rc->f); +} + + +static const char * eap_fast_parse_start(struct eap_fast_pac **pac) +{ + if (*pac) + return "START line without END"; + + *pac = os_zalloc(sizeof(struct eap_fast_pac)); + if (*pac == NULL) + return "No memory for PAC entry"; + (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; + return NULL; +} + + +static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac) +{ + if (*pac == NULL) + return "END line without START"; + if (*pac_root) { + struct eap_fast_pac *end = *pac_root; + while (end->next) + end = end->next; + end->next = *pac; + } else + *pac_root = *pac; + + *pac = NULL; + return NULL; +} + + +static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac, + char *pos) +{ + pac->pac_type = atoi(pos); + if (pac->pac_type != PAC_TYPE_TUNNEL_PAC && + pac->pac_type != PAC_TYPE_USER_AUTHORIZATION && + pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) + return "Unrecognized PAC-Type"; + + return NULL; +} + + +static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos) +{ + u8 *key; + size_t key_len; + + key = eap_fast_parse_hex(pos, &key_len); + if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { + os_free(key); + return "Invalid PAC-Key"; + } + + os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); + os_free(key); + + return NULL; +} + + +static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac, + char *pos) +{ + os_free(pac->pac_opaque); + pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len); + if (pac->pac_opaque == NULL) + return "Invalid PAC-Opaque"; + return NULL; +} + + +static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos) +{ + os_free(pac->a_id); + pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); + if (pac->a_id == NULL) + return "Invalid A-ID"; + return NULL; +} + + +static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos) +{ + os_free(pac->i_id); + pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); + if (pac->i_id == NULL) + return "Invalid I-ID"; + return NULL; +} + + +static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac, + char *pos) +{ + os_free(pac->a_id_info); + pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len); + if (pac->a_id_info == NULL) + return "Invalid A-ID-Info"; + return NULL; +} + + +/** + * eap_fast_load_pac - Load PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file) +{ + struct eap_fast_read_ctx rc; + struct eap_fast_pac *pac = NULL; + int count = 0; + char *pos; + const char *err = NULL; + + if (pac_file == NULL) + return -1; + + if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) + return 0; + + if (eap_fast_read_line(&rc, &pos) < 0 || + os_strcmp(pac_file_hdr, rc.buf) != 0) + err = "Unrecognized header line"; + + while (!err && eap_fast_read_line(&rc, &pos) == 0) { + if (os_strcmp(rc.buf, "START") == 0) + err = eap_fast_parse_start(&pac); + else if (os_strcmp(rc.buf, "END") == 0) { + err = eap_fast_parse_end(pac_root, &pac); + count++; + } else if (!pac) + err = "Unexpected line outside START/END block"; + else if (os_strcmp(rc.buf, "PAC-Type") == 0) + err = eap_fast_parse_pac_type(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Key") == 0) + err = eap_fast_parse_pac_key(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) + err = eap_fast_parse_pac_opaque(pac, pos); + else if (os_strcmp(rc.buf, "A-ID") == 0) + err = eap_fast_parse_a_id(pac, pos); + else if (os_strcmp(rc.buf, "I-ID") == 0) + err = eap_fast_parse_i_id(pac, pos); + else if (os_strcmp(rc.buf, "A-ID-Info") == 0) + err = eap_fast_parse_a_id_info(pac, pos); + } + + if (pac) { + err = "PAC block not terminated with END"; + eap_fast_free_pac(pac); + } + + eap_fast_deinit_pac_data(&rc); + + if (err) { + wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'", + err, pac_file, rc.line); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'", + count, pac_file); + + return 0; +} + + +static void eap_fast_write(char **buf, char **pos, size_t *buf_len, + const char *field, const u8 *data, + size_t len, int txt) +{ + size_t i, need; + int ret; + + if (data == NULL || *buf == NULL) + return; + + need = os_strlen(field) + len * 2 + 30; + if (txt) + need += os_strlen(field) + len + 20; + + if (*pos - *buf + need > *buf_len) { + char *nbuf = os_realloc(*buf, *buf_len + need); + if (nbuf == NULL) { + os_free(*buf); + *buf = NULL; + return; + } + *buf = nbuf; + *buf_len += need; + } + + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return; + *pos += ret; + *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len); + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return; + *pos += ret; + + if (txt) { + ret = os_snprintf(*pos, *buf + *buf_len - *pos, + "%s-txt=", field); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return; + *pos += ret; + for (i = 0; i < len; i++) { + ret = os_snprintf(*pos, *buf + *buf_len - *pos, + "%c", data[i]); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return; + *pos += ret; + } + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return; + *pos += ret; + } +} + + +static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file, + char *buf, size_t len) +{ + if (os_strncmp(pac_file, "blob://", 7) == 0) { + struct wpa_config_blob *blob; + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) + return -1; + blob->data = (u8 *) buf; + blob->len = len; + buf = NULL; + blob->name = os_strdup(pac_file + 7); + if (blob->name == NULL) { + os_free(blob); + return -1; + } + eap_set_config_blob(sm, blob); + } else { + FILE *f; + f = fopen(pac_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC " + "file '%s' for writing", pac_file); + return -1; + } + if (fwrite(buf, 1, len, f) != len) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all " + "PACs into '%s'", pac_file); + fclose(f); + return -1; + } + os_free(buf); + fclose(f); + } + + return 0; +} + + +static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, + char **pos, size_t *buf_len) +{ + int ret; + + ret = os_snprintf(*pos, *buf + *buf_len - *pos, + "START\nPAC-Type=%d\n", pac->pac_type); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return -1; + + *pos += ret; + eap_fast_write(buf, pos, buf_len, "PAC-Key", + pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0); + eap_fast_write(buf, pos, buf_len, "PAC-Opaque", + pac->pac_opaque, pac->pac_opaque_len, 0); + eap_fast_write(buf, pos, buf_len, "PAC-Info", + pac->pac_info, pac->pac_info_len, 0); + eap_fast_write(buf, pos, buf_len, "A-ID", + pac->a_id, pac->a_id_len, 0); + eap_fast_write(buf, pos, buf_len, "I-ID", + pac->i_id, pac->i_id_len, 1); + eap_fast_write(buf, pos, buf_len, "A-ID-Info", + pac->a_id_info, pac->a_id_info_len, 1); + if (*buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC " + "data"); + return -1; + } + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return -1; + *pos += ret; + + return 0; +} + + +/** + * eap_fast_save_pac - Save PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file) +{ + struct eap_fast_pac *pac; + int ret, count = 0; + char *buf, *pos; + size_t buf_len; + + if (pac_file == NULL) + return -1; + + buf_len = 1024; + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); + if (ret < 0 || ret >= buf + buf_len - pos) { + os_free(buf); + return -1; + } + pos += ret; + + pac = pac_root; + while (pac) { + if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) { + os_free(buf); + return -1; + } + count++; + pac = pac->next; + } + + if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'", + count, pac_file); + + return 0; +} + + +/** + * eap_fast_pac_list_truncate - Truncate a PAC list to the given length + * @pac_root: Root of the PAC list + * @max_len: Maximum length of the list (>= 1) + * Returns: Number of PAC entries removed + */ +size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, + size_t max_len) +{ + struct eap_fast_pac *pac, *prev; + size_t count; + + pac = pac_root; + prev = NULL; + count = 0; + + while (pac) { + count++; + if (count > max_len) + break; + prev = pac; + pac = pac->next; + } + + if (count <= max_len || prev == NULL) + return 0; + + count = 0; + prev->next = NULL; + + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + count++; + } + + return count; +} + + +static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) +{ + u8 *pos, *end; + u16 type, len; + + pos = pac->pac_info; + end = pos + pac->pac_info_len; + + while (pos + 4 < end) { + type = WPA_GET_BE16(pos); + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (type == PAC_TYPE_A_ID) { + os_free(pac->a_id); + pac->a_id = os_malloc(len); + if (pac->a_id == NULL) + break; + os_memcpy(pac->a_id, pos, len); + pac->a_id_len = len; + } + + if (type == PAC_TYPE_A_ID_INFO) { + os_free(pac->a_id_info); + pac->a_id_info = os_malloc(len); + if (pac->a_id_info == NULL) + break; + os_memcpy(pac->a_id_info, pos, len); + pac->a_id_info_len = len; + } + + pos += len; + } +} + + +/** + * eap_fast_load_pac_bin - Load PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file) +{ + const struct wpa_config_blob *blob = NULL; + u8 *buf, *end, *pos; + size_t len, count = 0; + struct eap_fast_pac *pac, *prev; + + *pac_root = NULL; + + if (pac_file == NULL) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + blob = eap_get_config_blob(sm, pac_file + 7); + if (blob == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file + 7); + return 0; + } + buf = blob->data; + len = blob->len; + } else { + buf = (u8 *) os_readfile(pac_file, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file); + return 0; + } + } + + if (len == 0) { + if (blob == NULL) + os_free(buf); + return 0; + } + + if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC || + WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)", + pac_file); + if (blob == NULL) + os_free(buf); + return -1; + } + + pac = prev = NULL; + pos = buf + 6; + end = buf + len; + while (pos < end) { + if (end - pos < 2 + 32 + 2 + 2) + goto parse_fail; + + pac = os_zalloc(sizeof(*pac)); + if (pac == NULL) + goto parse_fail; + + pac->pac_type = WPA_GET_BE16(pos); + pos += 2; + os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + pac->pac_opaque_len = WPA_GET_BE16(pos); + pos += 2; + if (pos + pac->pac_opaque_len + 2 > end) + goto parse_fail; + pac->pac_opaque = os_malloc(pac->pac_opaque_len); + if (pac->pac_opaque == NULL) + goto parse_fail; + os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); + pos += pac->pac_opaque_len; + pac->pac_info_len = WPA_GET_BE16(pos); + pos += 2; + if (pos + pac->pac_info_len > end) + goto parse_fail; + pac->pac_info = os_malloc(pac->pac_info_len); + if (pac->pac_info == NULL) + goto parse_fail; + os_memcpy(pac->pac_info, pos, pac->pac_info_len); + pos += pac->pac_info_len; + eap_fast_pac_get_a_id(pac); + + count++; + if (prev) + prev->next = pac; + else + *pac_root = pac; + prev = pac; + } + + if (blob == NULL) + os_free(buf); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)", + (unsigned long) count, pac_file); + + return 0; + +parse_fail: + wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)", + pac_file); + if (blob == NULL) + os_free(buf); + if (pac) + eap_fast_free_pac(pac); + return -1; +} + + +/** + * eap_fast_save_pac_bin - Save PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file) +{ + size_t len, count = 0; + struct eap_fast_pac *pac; + u8 *buf, *pos; + + len = 6; + pac = pac_root; + while (pac) { + if (pac->pac_opaque_len > 65535 || + pac->pac_info_len > 65535) + return -1; + len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len + + 2 + pac->pac_info_len; + pac = pac->next; + } + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + pos = buf; + WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC); + pos += 4; + WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION); + pos += 2; + + pac = pac_root; + while (pac) { + WPA_PUT_BE16(pos, pac->pac_type); + pos += 2; + os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + WPA_PUT_BE16(pos, pac->pac_opaque_len); + pos += 2; + os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); + pos += pac->pac_opaque_len; + WPA_PUT_BE16(pos, pac->pac_info_len); + pos += 2; + os_memcpy(pos, pac->pac_info, pac->pac_info_len); + pos += pac->pac_info_len; + + pac = pac->next; + count++; + } + + if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' " + "(bin)", (unsigned long) count, pac_file); + + return 0; +} diff --git a/src/eap_peer/eap_fast_pac.h b/src/eap_peer/eap_fast_pac.h new file mode 100644 index 0000000000000..9483f96852c65 --- /dev/null +++ b/src/eap_peer/eap_fast_pac.h @@ -0,0 +1,56 @@ +/* + * EAP peer method: EAP-FAST PAC file processing + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_FAST_PAC_H +#define EAP_FAST_PAC_H + +#include "eap_common/eap_fast_common.h" + +struct eap_fast_pac { + struct eap_fast_pac *next; + + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_opaque; + size_t pac_opaque_len; + u8 *pac_info; + size_t pac_info_len; + u8 *a_id; + size_t a_id_len; + u8 *i_id; + size_t i_id_len; + u8 *a_id_info; + size_t a_id_info_len; + u16 pac_type; +}; + + +void eap_fast_free_pac(struct eap_fast_pac *pac); +struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type); +int eap_fast_add_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + struct eap_fast_pac *entry); +int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file); +int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file); +size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, + size_t max_len); +int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file); +int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file); + +#endif /* EAP_FAST_PAC_H */ diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c new file mode 100644 index 0000000000000..9126e1c8de729 --- /dev/null +++ b/src/eap_peer/eap_gpsk.c @@ -0,0 +1,737 @@ +/* + * EAP peer method: EAP-GPSK (draft-ietf-emu-eap-gpsk-08.txt) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_gpsk_common.h" + +struct eap_gpsk_data { + enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; + u8 rand_server[EAP_GPSK_RAND_LEN]; + u8 rand_peer[EAP_GPSK_RAND_LEN]; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 sk[EAP_GPSK_MAX_SK_LEN]; + size_t sk_len; + u8 pk[EAP_GPSK_MAX_PK_LEN]; + size_t pk_len; + u8 session_id; + int session_id_set; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + int vendor; /* CSuite/Specifier */ + int specifier; /* CSuite/Specifier */ + u8 *psk; + size_t psk_len; +}; + + +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, + u8 identifier, + const u8 *csuite_list, + size_t csuite_list_len); +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, + u8 identifier); + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_gpsk_state_txt(int state) +{ + switch (state) { + case GPSK_1: + return "GPSK-1"; + case GPSK_3: + return "GPSK-3"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_gpsk_state(struct eap_gpsk_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", + eap_gpsk_state_txt(data->state), + eap_gpsk_state_txt(state)); + data->state = state; +} + + +static void eap_gpsk_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_gpsk_init(struct eap_sm *sm) +{ + struct eap_gpsk_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = GPSK_1; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->id_peer = os_malloc(identity_len); + if (data->id_peer == NULL) { + eap_gpsk_deinit(sm, data); + return NULL; + } + os_memcpy(data->id_peer, identity, identity_len); + data->id_peer_len = identity_len; + } + + data->psk = os_malloc(password_len); + if (data->psk == NULL) { + eap_gpsk_deinit(sm, data); + return NULL; + } + os_memcpy(data->psk, password, password_len); + data->psk_len = password_len; + + return data; +} + + +static void eap_gpsk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + os_free(data->id_server); + os_free(data->id_peer); + os_free(data->psk); + os_free(data); +} + + +static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + u16 alen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); + return NULL; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); + return NULL; + } + os_free(data->id_server); + data->id_server = os_malloc(alen); + if (data->id_server == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); + return NULL; + } + os_memcpy(data->id_server, pos, alen); + data->id_server_len = alen; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", + data->id_server, data->id_server_len); + pos += alen; + + return pos; +} + + +static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + if (pos == NULL) + return NULL; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); + return NULL; + } + os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", + data->rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + + return pos; +} + + +static int eap_gpsk_select_csuite(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 *csuite_list, + size_t csuite_list_len) +{ + struct eap_gpsk_csuite *csuite; + int i, count; + + count = csuite_list_len / sizeof(struct eap_gpsk_csuite); + data->vendor = EAP_GPSK_VENDOR_IETF; + data->specifier = EAP_GPSK_CIPHER_RESERVED; + csuite = (struct eap_gpsk_csuite *) csuite_list; + for (i = 0; i < count; i++) { + int vendor, specifier; + vendor = WPA_GET_BE32(csuite->vendor); + specifier = WPA_GET_BE16(csuite->specifier); + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", + i, vendor, specifier); + if (data->vendor == EAP_GPSK_VENDOR_IETF && + data->specifier == EAP_GPSK_CIPHER_RESERVED && + eap_gpsk_supported_ciphersuite(vendor, specifier)) { + data->vendor = vendor; + data->specifier = specifier; + } + csuite++; + } + if (data->vendor == EAP_GPSK_VENDOR_IETF && + data->specifier == EAP_GPSK_CIPHER_RESERVED) { + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " + "ciphersuite found"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", + data->vendor, data->specifier); + + return 0; +} + + +static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 **list, + size_t *list_len, + const u8 *pos, const u8 *end) +{ + if (pos == NULL) + return NULL; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); + return NULL; + } + *list_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < (int) *list_len) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); + return NULL; + } + if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", + (unsigned long) *list_len); + return NULL; + } + *list = pos; + pos += *list_len; + + if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0) + return NULL; + + return pos; +} + + +static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, + struct eap_gpsk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + size_t csuite_list_len; + const u8 *csuite_list, *pos, *end; + struct wpabuf *resp; + + if (data->state != GPSK_1) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); + + end = payload + payload_len; + + pos = eap_gpsk_process_id_server(data, payload, end); + pos = eap_gpsk_process_rand_server(data, pos, end); + pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, + &csuite_list_len, pos, end); + if (pos == NULL) { + eap_gpsk_state(data, FAILURE); + return NULL; + } + + resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), + csuite_list, csuite_list_len); + if (resp == NULL) + return NULL; + + eap_gpsk_state(data, GPSK_3); + + return resp; +} + + +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, + u8 identifier, + const u8 *csuite_list, + size_t csuite_list_len) +{ + struct wpabuf *resp; + size_t len, miclen; + u8 *rpos, *start; + struct eap_gpsk_csuite *csuite; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + + 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + + sizeof(struct eap_gpsk_csuite) + 2 + miclen; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, + EAP_CODE_RESPONSE, identifier); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); + start = wpabuf_put(resp, 0); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", + data->id_peer, data->id_peer_len); + wpabuf_put_be16(resp, data->id_peer_len); + wpabuf_put_data(resp, data->id_peer, data->id_peer_len); + + wpabuf_put_be16(resp, data->id_server_len); + wpabuf_put_data(resp, data->id_server, data->id_server_len); + + if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " + "for RAND_Peer"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", + data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); + + wpabuf_put_be16(resp, csuite_list_len); + wpabuf_put_data(resp, csuite_list, csuite_list_len); + + csuite = wpabuf_put(resp, sizeof(*csuite)); + WPA_PUT_BE32(csuite->vendor, data->vendor); + WPA_PUT_BE16(csuite->specifier, data->specifier); + + if (eap_gpsk_derive_keys(data->psk, data->psk_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + data->id_server, data->id_server_len, + data->msk, data->emsk, + data->sk, &data->sk_len, + data->pk, &data->pk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + /* No PD_Payload_1 */ + wpabuf_put_be16(resp, 0); + + rpos = wpabuf_put(resp, miclen); + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, rpos - start, rpos) < + 0) { + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + return resp; +} + + +static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "RAND_Peer"); + return NULL; + } + if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " + "GPSK-3 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", + data->rand_peer, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", + pos, EAP_GPSK_RAND_LEN); + return NULL; + } + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "RAND_Server"); + return NULL; + } + if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " + "GPSK-3 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", + data->rand_server, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", + pos, EAP_GPSK_RAND_LEN); + return NULL; + } + pos += EAP_GPSK_RAND_LEN; + + return pos; +} + + +static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + size_t len; + + if (pos == NULL) + return NULL; + + if (end - pos < (int) 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "length(ID_Server)"); + return NULL; + } + + len = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < (int) len) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "ID_Server"); + return NULL; + } + + if (len != data->id_server_len || + os_memcmp(pos, data->id_server, len) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " + "the one used in GPSK-1"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", + data->id_server, data->id_server_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", + pos, len); + return NULL; + } + + pos += len; + + return pos; +} + + +static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + int vendor, specifier; + const struct eap_gpsk_csuite *csuite; + + if (pos == NULL) + return NULL; + + if (end - pos < (int) sizeof(*csuite)) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "CSuite_Sel"); + return NULL; + } + csuite = (const struct eap_gpsk_csuite *) pos; + vendor = WPA_GET_BE32(csuite->vendor); + specifier = WPA_GET_BE16(csuite->specifier); + pos += sizeof(*csuite); + if (vendor != data->vendor || specifier != data->specifier) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " + "match with the one sent in GPSK-2 (%d:%d)", + vendor, specifier, data->vendor, data->specifier); + return NULL; + } + + return pos; +} + + +static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + u16 alen; + + if (pos == NULL) + return NULL; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "PD_Payload_2 length"); + return NULL; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "%d-octet PD_Payload_2", alen); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); + pos += alen; + + return pos; +} + + +static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, + const u8 *payload, + const u8 *pos, const u8 *end) +{ + size_t miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (pos == NULL) + return NULL; + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); + return NULL; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + return NULL; + } + if (os_memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + return NULL; + } + pos += miclen; + + return pos; +} + + +static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, + struct eap_gpsk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end; + + if (data->state != GPSK_3) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); + + end = payload + payload_len; + + pos = eap_gpsk_validate_rand(data, payload, end); + pos = eap_gpsk_validate_id_server(data, pos, end); + pos = eap_gpsk_validate_csuite(data, pos, end); + pos = eap_gpsk_validate_pd_payload_2(data, pos, end); + pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); + + if (pos == NULL) { + eap_gpsk_state(data, FAILURE); + return NULL; + } + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-2", + (unsigned long) (end - pos)); + } + + resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + eap_gpsk_state(data, SUCCESS); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + + return resp; +} + + +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, + u8 identifier) +{ + struct wpabuf *resp; + u8 *rpos, *start; + size_t mlen; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); + + mlen = eap_gpsk_mic_len(data->vendor, data->specifier); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, + EAP_CODE_RESPONSE, identifier); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); + start = wpabuf_put(resp, 0); + + /* No PD_Payload_3 */ + wpabuf_put_be16(resp, 0); + + rpos = wpabuf_put(resp, mlen); + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, rpos - start, rpos) < + 0) { + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + return resp; +} + + +static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_gpsk_data *data = priv; + struct wpabuf *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + switch (*pos) { + case EAP_GPSK_OPCODE_GPSK_1: + resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, + pos + 1, len - 1); + break; + case EAP_GPSK_OPCODE_GPSK_3: + resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, + pos + 1, len - 1); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " + "unknown opcode %d", *pos); + ret->ignore = TRUE; + return NULL; + } + + return resp; +} + + +static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_gpsk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); + if (eap == NULL) + return -1; + + eap->init = eap_gpsk_init; + eap->deinit = eap_gpsk_deinit; + eap->process = eap_gpsk_process; + eap->isKeyAvailable = eap_gpsk_isKeyAvailable; + eap->getKey = eap_gpsk_getKey; + eap->get_emsk = eap_gpsk_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c new file mode 100644 index 0000000000000..b2b554b4478b6 --- /dev/null +++ b/src/eap_peer/eap_gtc.c @@ -0,0 +1,151 @@ +/* + * EAP peer method: EAP-GTC (RFC 3748) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" + + +struct eap_gtc_data { + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && + sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } + return data; +} + + +static void eap_gtc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_gtc_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *password, *identity; + size_t password_len, identity_len, len, plen; + int otp; + u8 id; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + id = eap_get_id(reqData); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len); + if (data->prefix && + (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " + "expected prefix"); + + /* Send an empty response in order to allow tunneled + * acknowledgement of the failure. This will also cover the + * error case which seems to use EAP-MSCHAPv2 like error + * reporting with EAP-GTC inside EAP-FAST tunnel. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, + 0, EAP_CODE_RESPONSE, id); + return resp; + } + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + plen = password_len; + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + if (data->prefix) + plen += 9 + identity_len + 1; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + if (data->prefix) { + wpabuf_put_data(resp, "RESPONSE=", 9); + wpabuf_put_data(resp, identity, identity_len); + wpabuf_put_u8(resp, '\0'); + } + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", + wpabuf_head_u8(resp) + sizeof(struct eap_hdr) + + 1, plen); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_gtc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); + if (eap == NULL) + return -1; + + eap->init = eap_gtc_init; + eap->deinit = eap_gtc_deinit; + eap->process = eap_gtc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h new file mode 100644 index 0000000000000..e7c826ee8feac --- /dev/null +++ b/src/eap_peer/eap_i.h @@ -0,0 +1,355 @@ +/* + * EAP peer state machines internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpabuf.h" +#include "eap_peer/eap.h" +#include "eap_common/eap_common.h" + +/* RFC 4137 - EAP Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ +struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ + Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ + EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ + EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ + Boolean allowNotifications; +}; + + +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 4.4 of RFC 4137. + */ +struct eap_method { + /** + * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + */ + int vendor; + + /** + * method - EAP type number (EAP_TYPE_*) + */ + EapType method; + + /** + * name - Name of the method (e.g., "TLS") + */ + const char *name; + + /** + * init - Initialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to allocated private data, or %NULL on failure + * + * This function is used to initialize the EAP method explicitly + * instead of using METHOD_INIT state as specific in RFC 4137. The + * method is expected to initialize it method-specific state and return + * a pointer that will be used as the priv argument to other calls. + */ + void * (*init)(struct eap_sm *sm); + + /** + * deinit - Deinitialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * Deinitialize the EAP method and free any allocated private data. + */ + void (*deinit)(struct eap_sm *sm, void *priv); + + /** + * process - Process an EAP request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) + * + * This function is a combination of m.check(), m.process(), and + * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other + * words, this function validates the incoming request, processes it, + * and build a response packet. m.check() and m.process() return values + * are returned through struct eap_method_ret *ret variable. Caller is + * responsible for freeing the returned EAP response packet. + */ + struct wpabuf * (*process)(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData); + + /** + * isKeyAvailable - Find out whether EAP method has keying material + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE if key material (eapKeyData) is available + */ + Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + + /** + * getKey - Get EAP method specific keying material (eapKeyData) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to variable to store key length (eapKeyDataLen) + * Returns: Keying material (eapKeyData) or %NULL if not available + * + * This function can be used to get the keying material from the EAP + * method. The key may already be stored in the method-specific private + * data or this function may derive the key. + */ + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * get_status - Get EAP method status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf + * + * Query EAP method for status information. This function fills in a + * text area with current status information from the EAP method. If + * the buffer (buf) is not large enough, status information will be + * truncated to fit the buffer. + */ + int (*get_status)(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose); + + /** + * has_reauth_data - Whether method is ready for fast reauthentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE or %FALSE based on whether fast reauthentication is + * possible + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. + */ + Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + + /** + * deinit_for_reauth - Release data that is not needed for fast re-auth + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when authentication has been completed and EAP state machine is + * requesting that enough state information is maintained for fast + * re-authentication + */ + void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * init_for_reauth - Prepare for start of fast re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when EAP authentication is started and EAP state machine is + * requesting fast re-authentication to be used. + */ + void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * get_identity - Get method specific identity for re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Length of the returned identity + * Returns: Pointer to the method specific identity or %NULL if default + * identity is to be used + * + * This function is an optional handler that only EAP methods + * that use method specific identity need to implement. + */ + const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_peer_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_PEER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP peer method interface + * + * The EAP peer method implementation should set this variable to + * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + /** + * dl_handle - Handle for the dynamic library + * + * This variable is used internally in the EAP method registration code + * to store a handle for the dynamic library. If the method is linked + * in statically, this is %NULL. + */ + void *dl_handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); +}; + + +/** + * struct eap_sm - EAP state machine data + */ +struct eap_sm { + enum { + EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, + EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, + EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, + EAP_FAILURE + } EAP_state; + /* Long-term local variables */ + EapType selectedMethod; + EapMethodState methodState; + int lastId; + struct wpabuf *lastRespData; + EapDecision decision; + /* Short-term local variables */ + Boolean rxReq; + Boolean rxSuccess; + Boolean rxFailure; + int reqId; + EapType reqMethod; + int reqVendor; + u32 reqVendorMethod; + Boolean ignore; + /* Constants */ + int ClientTimeout; + + /* Miscellaneous variables */ + Boolean allowNotifications; /* peer state machine <-> methods */ + struct wpabuf *eapRespData; /* peer to lower layer */ + Boolean eapKeyAvailable; /* peer to lower layer */ + u8 *eapKeyData; /* peer to lower layer */ + size_t eapKeyDataLen; /* peer to lower layer */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in RFC 4137 */ + Boolean changed; + void *eapol_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + int init_phase2; + int fast_reauth; + + Boolean rxResp /* LEAP only */; + Boolean leap_done; + Boolean peap_done; + u8 req_md5[16]; /* MD5() of the current EAP packet */ + u8 last_md5[16]; /* MD5() of the previously received EAP packet; used + * in duplicate request detection. */ + + void *msg_ctx; + void *scard_ctx; + void *ssl_ctx; + + unsigned int workaround; + + /* Optional challenges generated in Phase 1 (EAP-FAST) */ + u8 *peer_challenge, *auth_challenge; + + int num_rounds; + int force_disabled; + + struct wps_context *wps; + + int prev_failure; +}; + +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash); +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len); +void eap_clear_config_otp(struct eap_sm *sm); +const char * eap_get_config_phase1(struct eap_sm *sm); +const char * eap_get_config_phase2(struct eap_sm *sm); +struct eap_peer_config * eap_get_config(struct eap_sm *sm); +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob); +const struct wpa_config_blob * +eap_get_config_blob(struct eap_sm *sm, const char *name); +void eap_notify_pending(struct eap_sm *sm); +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method); + +#endif /* EAP_I_H */ diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c new file mode 100644 index 0000000000000..bb49a662d72d0 --- /dev/null +++ b/src/eap_peer/eap_ikev2.c @@ -0,0 +1,506 @@ +/* + * EAP-IKEv2 peer (RFC 5106) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_common/eap_ikev2_common.h" +#include "ikev2.h" + + +struct eap_ikev2_data { + struct ikev2_responder_data ikev2; + enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; + int keys_ready; + u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; + int keymat_ok; +}; + + +static const char * eap_ikev2_state_txt(int state) +{ + switch (state) { + case WAIT_START: + return "WAIT_START"; + case PROC_MSG: + return "PROC_MSG"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_ikev2_state(struct eap_ikev2_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", + eap_ikev2_state_txt(data->state), + eap_ikev2_state_txt(state)); + data->state = state; +} + + +static void * eap_ikev2_init(struct eap_sm *sm) +{ + struct eap_ikev2_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) { + wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = WAIT_START; + data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->ikev2.state = SA_INIT; + data->ikev2.peer_auth = PEER_AUTH_SECRET; + data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); + if (data->ikev2.key_pad == NULL) + goto failed; + data->ikev2.key_pad_len = 21; + data->ikev2.IDr = os_malloc(identity_len); + if (data->ikev2.IDr == NULL) + goto failed; + os_memcpy(data->ikev2.IDr, identity, identity_len); + data->ikev2.IDr_len = identity_len; + + password = eap_get_config_password(sm, &password_len); + if (password) { + data->ikev2.shared_secret = os_malloc(password_len); + if (data->ikev2.shared_secret == NULL) + goto failed; + os_memcpy(data->ikev2.shared_secret, password, password_len); + data->ikev2.shared_secret_len = password_len; + } + + return data; + +failed: + ikev2_responder_deinit(&data->ikev2); + os_free(data); + return NULL; +} + + +static void eap_ikev2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + ikev2_responder_deinit(&data->ikev2); + os_free(data); +} + + +static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data) +{ + if (eap_ikev2_derive_keymat( + data->ikev2.proposal.prf, &data->ikev2.keys, + data->ikev2.i_nonce, data->ikev2.i_nonce_len, + data->ikev2.r_nonce, data->ikev2.r_nonce_len, + data->keymat) < 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " + "derive key material"); + return -1; + } + data->keymat_ok = 1; + return 0; +} + + +static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen, icv_len = 0; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response"); + ret->allowNotifications = TRUE; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= IKEV2_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } +#ifdef CCNS_PL + /* Some issues figuring out the length of the message if Message Length + * field not included?! */ + if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; +#endif /* CCNS_PL */ + + plen = 1 + send_len; + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + plen += 4; + if (data->keys_ready) { + const struct ikev2_integ_alg *integ; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " + "Data"); + flags |= IKEV2_FLAGS_ICV_INCLUDED; + integ = ikev2_get_integ(data->ikev2.proposal.integ); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot generate ICV"); + return NULL; + } + icv_len = integ->hash_len; + + plen += icv_len; + } + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + const u8 *msg = wpabuf_head(resp); + size_t len = wpabuf_len(resp); + ikev2_integ_hash(data->ikev2.proposal.integ, + data->ikev2.keys.SK_ar, + data->ikev2.keys.SK_integ_len, + msg, len, wpabuf_put(resp, icv_len)); + } + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + switch (data->ikev2.state) { + case SA_AUTH: + /* SA_INIT was sent out, so message have to be + * integrity protected from now on. */ + data->keys_ready = 1; + break; + case IKEV2_DONE: + ret->methodState = METHOD_DONE; + if (data->state == FAIL) + break; + ret->decision = DECISION_COND_SUCC; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " + "completed successfully"); + if (eap_ikev2_peer_keymat(data)) + break; + eap_ikev2_state(data, DONE); + break; + case IKEV2_FAILED: + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " + "failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + break; + default: + break; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_ikev2_state(data, WAIT_FRAG_ACK); + } + + return resp; +} + + +static int eap_ikev2_process_icv(struct eap_ikev2_data *data, + const struct wpabuf *reqData, + u8 flags, const u8 *pos, const u8 **end) +{ + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + int icv_len = eap_ikev2_validate_icv( + data->ikev2.proposal.integ, &data->ikev2.keys, 1, + reqData, pos, *end); + if (icv_len < 0) + return -1; + /* Hide Integrity Checksum Data from further processing */ + *end -= icv_len; + } else if (data->keys_ready) { + wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " + "included integrity checksum"); + return -1; + } + + return 0; +} + + +static int eap_ikev2_process_cont(struct eap_ikev2_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); + eap_ikev2_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting " + "for %lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, + u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " + "a fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_ikev2_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 flags, id; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + start = pos; + end = start + len; + + if (len == 0) + flags = 0; /* fragment ack */ + else + flags = *pos++; + + if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { +#ifdef CCNS_PL + if (len > 1) /* Empty Flags field included in ACK */ +#else /* CCNS_PL */ + if (len != 0) +#endif /* CCNS_PL */ + { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " + "in WAIT_FRAG_ACK state"); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); + eap_ikev2_state(data, PROC_MSG); + return eap_ikev2_build_msg(data, ret, id); + } + + if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { + return eap_ikev2_process_fragment(data, ret, id, flags, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) { + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + eap_ikev2_state(data, FAIL); + return NULL; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + + if (data->out_buf == NULL) { + data->out_buf = ikev2_responder_build(&data->ikev2); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate " + "IKEv2 message"); + return NULL; + } + data->out_used = 0; + } + + eap_ikev2_state(data, PROC_MSG); + return eap_ikev2_build_msg(data, ret, id); +} + + +static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE && data->keymat_ok; +} + + +static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key) { + os_memcpy(key, data->keymat, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + } + + return key; +} + + +static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key) { + os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + } + + return key; +} + + +int eap_peer_ikev2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IKEV2, + "IKEV2"); + if (eap == NULL) + return -1; + + eap->init = eap_ikev2_init; + eap->deinit = eap_ikev2_deinit; + eap->process = eap_ikev2_process; + eap->isKeyAvailable = eap_ikev2_isKeyAvailable; + eap->getKey = eap_ikev2_getKey; + eap->get_emsk = eap_ikev2_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c new file mode 100644 index 0000000000000..01c1f160abce2 --- /dev/null +++ b/src/eap_peer/eap_leap.c @@ -0,0 +1,403 @@ +/* + * EAP peer method: LEAP + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "ms_funcs.h" +#include "crypto.h" + +#define LEAP_VERSION 1 +#define LEAP_CHALLENGE_LEN 8 +#define LEAP_RESPONSE_LEN 24 +#define LEAP_KEY_LEN 16 + + +struct eap_leap_data { + enum { + LEAP_WAIT_CHALLENGE, + LEAP_WAIT_SUCCESS, + LEAP_WAIT_RESPONSE, + LEAP_DONE + } state; + + u8 peer_challenge[LEAP_CHALLENGE_LEN]; + u8 peer_response[LEAP_RESPONSE_LEN]; + + u8 ap_challenge[LEAP_CHALLENGE_LEN]; + u8 ap_response[LEAP_RESPONSE_LEN]; +}; + + +static void * eap_leap_init(struct eap_sm *sm) +{ + struct eap_leap_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = LEAP_WAIT_CHALLENGE; + + sm->leap_done = FALSE; + return data; +} + + +static void eap_leap_deinit(struct eap_sm *sm, void *priv) +{ + os_free(priv); +} + + +static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *challenge, *identity, *password; + u8 challenge_len, *rpos; + size_t identity_len, password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + challenge_len = *pos++; + if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " + "(challenge_len=%d reqDataLen=%lu)", + challenge_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + challenge = pos; + os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", + challenge, LEAP_CHALLENGE_LEN); + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_RESPONSE_LEN + identity_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); + rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); + if (pwhash) + challenge_response(challenge, password, rpos); + else + nt_challenge_response(challenge, password, password_len, rpos); + os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", + rpos, LEAP_RESPONSE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_SUCCESS; + + return resp; +} + + +static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + u8 *pos; + const u8 *identity; + size_t identity_len; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + + if (data->state != LEAP_WAIT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_CHALLENGE_LEN + identity_len, + EAP_CODE_REQUEST, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); + pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); + if (os_get_random(pos, LEAP_CHALLENGE_LEN)) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " + "for challenge"); + wpabuf_free(resp); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, + LEAP_CHALLENGE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_RESPONSE; + + return resp; +} + + +static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + const u8 *pos, *password; + u8 response_len, pw_hash[16], pw_hash_hash[16], + expected[LEAP_RESPONSE_LEN]; + size_t password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); + + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + response_len = *pos++; + if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " + "(response_len=%d reqDataLen=%lu)", + response_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", + pos, LEAP_RESPONSE_LEN); + os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); + + if (pwhash) { + hash_nt_password_hash(password, pw_hash_hash); + } else { + nt_password_hash(password, password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + } + challenge_response(data->ap_challenge, pw_hash_hash, expected); + + ret->methodState = METHOD_DONE; + ret->allowNotifications = FALSE; + + if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " + "response - authentication failed"); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", + expected, LEAP_RESPONSE_LEN); + ret->decision = DECISION_FAIL; + return NULL; + } + + ret->decision = DECISION_UNCOND_SUCC; + + /* LEAP is somewhat odd method since it sends EAP-Success in the middle + * of the authentication. Use special variable to transit EAP state + * machine to SUCCESS state. */ + sm->leap_done = TRUE; + data->state = LEAP_DONE; + + /* No more authentication messages expected; AP will send EAPOL-Key + * frames if encryption is enabled. */ + return NULL; +} + + +static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *eap; + size_t password_len; + const u8 *password; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); + eap_sm_request_password(sm); + ret->ignore = TRUE; + return NULL; + } + + /* + * LEAP needs to be able to handle EAP-Success frame which does not + * include Type field. Consequently, eap_hdr_validate() cannot be used + * here. This validation will be done separately for EAP-Request and + * EAP-Response frames. + */ + eap = wpabuf_head(reqData); + if (wpabuf_len(reqData) < sizeof(*eap) || + be_to_host16(eap->length) > wpabuf_len(reqData)) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->allowNotifications = TRUE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + sm->leap_done = FALSE; + + switch (eap->code) { + case EAP_CODE_REQUEST: + return eap_leap_process_request(sm, priv, ret, reqData); + case EAP_CODE_SUCCESS: + return eap_leap_process_success(sm, priv, ret, reqData); + case EAP_CODE_RESPONSE: + return eap_leap_process_response(sm, priv, ret, reqData); + default: + wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " + "ignored", eap->code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_leap_data *data = priv; + return data->state == LEAP_DONE; +} + + +static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_leap_data *data = priv; + u8 *key, pw_hash_hash[16], pw_hash[16]; + const u8 *addr[5], *password; + size_t elen[5], password_len; + int pwhash; + + if (data->state != LEAP_DONE) + return NULL; + + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (password == NULL) + return NULL; + + key = os_malloc(LEAP_KEY_LEN); + if (key == NULL) + return NULL; + + if (pwhash) + hash_nt_password_hash(password, pw_hash_hash); + else { + nt_password_hash(password, password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", + pw_hash_hash, 16); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", + data->peer_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", + data->peer_response, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", + data->ap_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", + data->ap_response, LEAP_RESPONSE_LEN); + + addr[0] = pw_hash_hash; + elen[0] = 16; + addr[1] = data->ap_challenge; + elen[1] = LEAP_CHALLENGE_LEN; + addr[2] = data->ap_response; + elen[2] = LEAP_RESPONSE_LEN; + addr[3] = data->peer_challenge; + elen[3] = LEAP_CHALLENGE_LEN; + addr[4] = data->peer_response; + elen[4] = LEAP_RESPONSE_LEN; + md5_vector(5, addr, elen, key); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); + *len = LEAP_KEY_LEN; + + return key; +} + + +int eap_peer_leap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_leap_init; + eap->deinit = eap_leap_deinit; + eap->process = eap_leap_process; + eap->isKeyAvailable = eap_leap_isKeyAvailable; + eap->getKey = eap_leap_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c new file mode 100644 index 0000000000000..7961143a00c9e --- /dev/null +++ b/src/eap_peer/eap_md5.c @@ -0,0 +1,120 @@ +/* + * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_common/chap.h" + + +static void * eap_md5_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_md5_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *challenge, *password; + u8 *rpos, id; + size_t len, challenge_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + eap_sm_request_password(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len); + if (pos == NULL || len == 0) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + /* + * CHAP Challenge: + * Value-Size (1 octet) | Value(Challenge) | Name(optional) + */ + challenge_len = *pos++; + if (challenge_len == 0 || challenge_len > len - 1) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " + "(challenge_len=%lu len=%lu)", + (unsigned long) challenge_len, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + ret->ignore = FALSE; + challenge = pos; + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", + challenge, challenge_len); + + wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = TRUE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + /* + * CHAP Response: + * Value-Size (1 octet) | Value(Response) | Name(optional) + */ + wpabuf_put_u8(resp, CHAP_MD5_LEN); + + id = eap_get_id(resp); + rpos = wpabuf_put(resp, CHAP_MD5_LEN); + chap_md5(id, password, password_len, challenge, challenge_len, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); + + return resp; +} + + +int eap_peer_md5_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); + if (eap == NULL) + return -1; + + eap->init = eap_md5_init; + eap->deinit = eap_md5_deinit; + eap->process = eap_md5_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c new file mode 100644 index 0000000000000..2374e5e4225c6 --- /dev/null +++ b/src/eap_peer/eap_methods.c @@ -0,0 +1,528 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifdef CONFIG_DYNAMIC_EAP_METHODS +#include <dlfcn.h> +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + +#include "common.h" +#include "eap_i.h" +#include "eap_methods.h" + + +static struct eap_method *eap_methods = NULL; + + +/** + * eap_peer_get_eap_method - Get EAP method based on type number + * @vendor: EAP Vendor-Id (0 = IETF) + * @method: EAP type number + * Returns: Pointer to EAP method or %NULL if not found + */ +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + + +/** + * eap_peer_get_type - Get EAP type for the given EAP method name + * @name: EAP method name, e.g., TLS + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers based on the list of + * EAP methods included in the build. + */ +EapType eap_peer_get_type(const char *name, int *vendor) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (os_strcmp(m->name, name) == 0) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_get_name - Get EAP method name for the given EAP type + * @vendor: EAP Vendor-Id (0 = IETF) + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or %NULL if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_get_name(int vendor, EapType type) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == type) + return m->name; + } + return NULL; +} + + +/** + * eap_get_names - Get space separated list of names for supported EAP methods + * @buf: Buffer for names + * @buflen: Buffer length + * Returns: Number of characters written into buf (not including nul + * termination) + */ +size_t eap_get_names(char *buf, size_t buflen) +{ + char *pos, *end; + struct eap_method *m; + int ret; + + if (buflen == 0) + return 0; + + pos = buf; + end = pos + buflen; + + for (m = eap_methods; m; m = m->next) { + ret = os_snprintf(pos, end - pos, "%s%s", + m == eap_methods ? "" : " ", m->name); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + buf[buflen - 1] = '\0'; + + return pos - buf; +} + + +/** + * eap_get_names_as_string_array - Get supported EAP methods as string array + * @num: Buffer for returning the number of items in array, not including %NULL + * terminator. This parameter can be %NULL if the length is not needed. + * Returns: A %NULL-terminated array of strings, or %NULL on error. + * + * This function returns the list of names for all supported EAP methods as an + * array of strings. The caller must free the returned array items and the + * array. + */ +char ** eap_get_names_as_string_array(size_t *num) +{ + struct eap_method *m; + size_t array_len = 0; + char **array; + int i = 0, j; + + for (m = eap_methods; m; m = m->next) + array_len++; + + array = os_zalloc(sizeof(char *) * (array_len + 1)); + if (array == NULL) + return NULL; + + for (m = eap_methods; m; m = m->next) { + array[i++] = os_strdup(m->name); + if (array[i - 1] == NULL) { + for (j = 0; j < i; j++) + os_free(array[j]); + os_free(array); + return NULL; + } + } + array[i] = NULL; + + if (num) + *num = array_len; + + return array; +} + + +/** + * eap_peer_get_methods - Get a list of enabled EAP peer methods + * @count: Set to number of available methods + * Returns: List of enabled EAP peer methods + */ +const struct eap_method * eap_peer_get_methods(size_t *count) +{ + int c = 0; + struct eap_method *m; + + for (m = eap_methods; m; m = m->next) + c++; + + *count = c; + return eap_methods; +} + + +#ifdef CONFIG_DYNAMIC_EAP_METHODS +/** + * eap_peer_method_load - Load a dynamic EAP method library (shared object) + * @so: File path for the shared object file to load + * Returns: 0 on success, -1 on failure + */ +int eap_peer_method_load(const char *so) +{ + void *handle; + int (*dyn_init)(void); + int ret; + + handle = dlopen(so, RTLD_LAZY); + if (handle == NULL) { + wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method " + "'%s': %s", so, dlerror()); + return -1; + } + + dyn_init = dlsym(handle, "eap_peer_method_dynamic_init"); + if (dyn_init == NULL) { + dlclose(handle); + wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no " + "eap_peer_method_dynamic_init()", so); + return -1; + } + + ret = dyn_init(); + if (ret) { + dlclose(handle); + wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - " + "ret %d", so, ret); + return ret; + } + + /* Store the handle for this shared object. It will be freed with + * dlclose() when the EAP method is unregistered. */ + eap_methods->dl_handle = handle; + + wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so); + + return 0; +} + + +/** + * eap_peer_method_unload - Unload a dynamic EAP method library (shared object) + * @method: Pointer to the dynamically loaded EAP method + * Returns: 0 on success, -1 on failure + * + * This function can be used to unload EAP methods that have been previously + * loaded with eap_peer_method_load(). Before unloading the method, all + * references to the method must be removed to make sure that no dereferences + * of freed memory will occur after unloading. + */ +int eap_peer_method_unload(struct eap_method *method) +{ + struct eap_method *m, *prev; + void *handle; + + m = eap_methods; + prev = NULL; + while (m) { + if (m == method) + break; + prev = m; + m = m->next; + } + + if (m == NULL || m->dl_handle == NULL) + return -1; + + if (prev) + prev->next = m->next; + else + eap_methods = m->next; + + handle = m->dl_handle; + + if (m->free) + m->free(m); + else + eap_peer_method_free(m); + + dlclose(handle); + + return 0; +} +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + +/** + * eap_peer_method_alloc - Allocate EAP peer method structure + * @version: Version of the EAP peer method interface (set to + * EAP_PEER_METHOD_INTERFACE_VERSION) + * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + * @method: EAP type number (EAP_TYPE_*) + * @name: Name of the method (e.g., "TLS") + * Returns: Allocated EAP method structure or %NULL on failure + * + * The returned structure should be freed with eap_peer_method_free() when it + * is not needed anymore. + */ +struct eap_method * eap_peer_method_alloc(int version, int vendor, + EapType method, const char *name) +{ + struct eap_method *eap; + eap = os_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->version = version; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + + +/** + * eap_peer_method_free - Free EAP peer method structure + * @method: Method structure allocated with eap_peer_method_alloc() + */ +void eap_peer_method_free(struct eap_method *method) +{ + os_free(method); +} + + +/** + * eap_peer_method_register - Register an EAP peer method + * @method: EAP method to register + * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method + * has already been registered + * + * Each EAP peer method needs to call this function to register itself as a + * supported EAP method. + */ +int eap_peer_method_register(struct eap_method *method) +{ + struct eap_method *m, *last = NULL; + + if (method == NULL || method->name == NULL || + method->version != EAP_PEER_METHOD_INTERFACE_VERSION) + return -1; + + for (m = eap_methods; m; m = m->next) { + if ((m->vendor == method->vendor && + m->method == method->method) || + os_strcmp(m->name, method->name) == 0) + return -2; + last = m; + } + + if (last) + last->next = method; + else + eap_methods = method; + + return 0; +} + + +/** + * eap_peer_register_methods - Register statically linked EAP peer methods + * Returns: 0 on success, -1 on failure + * + * This function is called at program initialization to register all EAP peer + * methods that were linked in statically. + */ +int eap_peer_register_methods(void) +{ + int ret = 0; + +#ifdef EAP_MD5 + if (ret == 0) { + int eap_peer_md5_register(void); + ret = eap_peer_md5_register(); + } +#endif /* EAP_MD5 */ + +#ifdef EAP_TLS + if (ret == 0) { + int eap_peer_tls_register(void); + ret = eap_peer_tls_register(); + } +#endif /* EAP_TLS */ + +#ifdef EAP_MSCHAPv2 + if (ret == 0) { + int eap_peer_mschapv2_register(void); + ret = eap_peer_mschapv2_register(); + } +#endif /* EAP_MSCHAPv2 */ + +#ifdef EAP_PEAP + if (ret == 0) { + int eap_peer_peap_register(void); + ret = eap_peer_peap_register(); + } +#endif /* EAP_PEAP */ + +#ifdef EAP_TTLS + if (ret == 0) { + int eap_peer_ttls_register(void); + ret = eap_peer_ttls_register(); + } +#endif /* EAP_TTLS */ + +#ifdef EAP_GTC + if (ret == 0) { + int eap_peer_gtc_register(void); + ret = eap_peer_gtc_register(); + } +#endif /* EAP_GTC */ + +#ifdef EAP_OTP + if (ret == 0) { + int eap_peer_otp_register(void); + ret = eap_peer_otp_register(); + } +#endif /* EAP_OTP */ + +#ifdef EAP_SIM + if (ret == 0) { + int eap_peer_sim_register(void); + ret = eap_peer_sim_register(); + } +#endif /* EAP_SIM */ + +#ifdef EAP_LEAP + if (ret == 0) { + int eap_peer_leap_register(void); + ret = eap_peer_leap_register(); + } +#endif /* EAP_LEAP */ + +#ifdef EAP_PSK + if (ret == 0) { + int eap_peer_psk_register(void); + ret = eap_peer_psk_register(); + } +#endif /* EAP_PSK */ + +#ifdef EAP_AKA + if (ret == 0) { + int eap_peer_aka_register(void); + ret = eap_peer_aka_register(); + } +#endif /* EAP_AKA */ + +#ifdef EAP_AKA_PRIME + if (ret == 0) { + int eap_peer_aka_prime_register(void); + ret = eap_peer_aka_prime_register(); + } +#endif /* EAP_AKA_PRIME */ + +#ifdef EAP_FAST + if (ret == 0) { + int eap_peer_fast_register(void); + ret = eap_peer_fast_register(); + } +#endif /* EAP_FAST */ + +#ifdef EAP_PAX + if (ret == 0) { + int eap_peer_pax_register(void); + ret = eap_peer_pax_register(); + } +#endif /* EAP_PAX */ + +#ifdef EAP_SAKE + if (ret == 0) { + int eap_peer_sake_register(void); + ret = eap_peer_sake_register(); + } +#endif /* EAP_SAKE */ + +#ifdef EAP_GPSK + if (ret == 0) { + int eap_peer_gpsk_register(void); + ret = eap_peer_gpsk_register(); + } +#endif /* EAP_GPSK */ + +#ifdef EAP_WSC + if (ret == 0) { + int eap_peer_wsc_register(void); + ret = eap_peer_wsc_register(); + } +#endif /* EAP_WSC */ + +#ifdef EAP_IKEV2 + if (ret == 0) { + int eap_peer_ikev2_register(void); + ret = eap_peer_ikev2_register(); + } +#endif /* EAP_IKEV2 */ + +#ifdef EAP_VENDOR_TEST + if (ret == 0) { + int eap_peer_vendor_test_register(void); + ret = eap_peer_vendor_test_register(); + } +#endif /* EAP_VENDOR_TEST */ + +#ifdef EAP_TNC + if (ret == 0) { + int eap_peer_tnc_register(void); + ret = eap_peer_tnc_register(); + } +#endif /* EAP_TNC */ + + return ret; +} + + +/** + * eap_peer_unregister_methods - Unregister EAP peer methods + * + * This function is called at program termination to unregister all EAP peer + * methods. + */ +void eap_peer_unregister_methods(void) +{ + struct eap_method *m; +#ifdef CONFIG_DYNAMIC_EAP_METHODS + void *handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + handle = m->dl_handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + if (m->free) + m->free(m); + else + eap_peer_method_free(m); + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + if (handle) + dlclose(handle); +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + } +} diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h new file mode 100644 index 0000000000000..c11bd8c395dc3 --- /dev/null +++ b/src/eap_peer/eap_methods.h @@ -0,0 +1,87 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_METHODS_H +#define EAP_METHODS_H + +#include "eap_common/eap_defs.h" + +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method); +const struct eap_method * eap_peer_get_methods(size_t *count); + +struct eap_method * eap_peer_method_alloc(int version, int vendor, + EapType method, const char *name); +void eap_peer_method_free(struct eap_method *method); +int eap_peer_method_register(struct eap_method *method); + + +#ifdef IEEE8021X_EAPOL + +EapType eap_peer_get_type(const char *name, int *vendor); +const char * eap_get_name(int vendor, EapType type); +size_t eap_get_names(char *buf, size_t buflen); +char ** eap_get_names_as_string_array(size_t *num); +int eap_peer_register_methods(void); +void eap_peer_unregister_methods(void); + +#else /* IEEE8021X_EAPOL */ + +static inline EapType eap_peer_get_type(const char *name, int *vendor) +{ + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + +static inline const char * eap_get_name(int vendor, EapType type) +{ + return NULL; +} + +static inline size_t eap_get_names(char *buf, size_t buflen) +{ + return 0; +} + +static inline int eap_peer_register_methods(void) +{ + return 0; +} + +static inline void eap_peer_unregister_methods(void) +{ +} + +#endif /* IEEE8021X_EAPOL */ + + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + +int eap_peer_method_load(const char *so); +int eap_peer_method_unload(struct eap_method *method); + +#else /* CONFIG_DYNAMIC_EAP_METHODS */ + +static inline int eap_peer_method_load(const char *so) +{ + return 0; +} + +static inline int eap_peer_method_unload(struct eap_method *method) +{ + return 0; +} + +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + +#endif /* EAP_METHODS_H */ diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c new file mode 100644 index 0000000000000..b0c3ab77632b1 --- /dev/null +++ b/src/eap_peer/eap_mschapv2.c @@ -0,0 +1,877 @@ +/* + * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). + * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP + * Extensions Protocol, Version 2, for mutual authentication and key + * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in + * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in + * RFC 3079. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_config.h" +#include "ms_funcs.h" +#include "wpa_ctrl.h" +#include "mschapv2.h" + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_mschapv2_hdr { + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* usually same as EAP identifier; must be changed + * for challenges, but not for success/failure */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} STRUCT_PACKED; + +/* Response Data field */ +struct ms_response { + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags; +} STRUCT_PACKED; + +/* Change-Password Data field */ +struct ms_change_password { + u8 encr_password[516]; + u8 encr_hash[16]; + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags[2]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +struct eap_mschapv2_data { + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + /* Optional challenge values generated in EAP-FAST Phase 1 negotiation + */ + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; + int master_key_valid; + int success; + + struct wpabuf *prev_challenge; +}; + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->peer_challenge) { + data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->peer_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->peer_challenge, sm->peer_challenge, + MSCHAPV2_CHAL_LEN); + } + + if (sm->auth_challenge) { + data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->auth_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->auth_challenge, sm->auth_challenge, + MSCHAPV2_CHAL_LEN); + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + os_free(data->peer_challenge); + os_free(data->auth_challenge); + wpabuf_free(data->prev_challenge); + os_free(data); +} + + +static struct wpabuf * eap_mschapv2_challenge_reply( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, + u8 mschapv2_id, const u8 *auth_challenge) +{ + struct wpabuf *resp; + struct eap_mschapv2_hdr *ms; + u8 *peer_challenge; + int ms_len; + struct ms_response *r; + size_t identity_len, password_len; + const u8 *identity, *password; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_RESPONSE; + ms->mschapv2_id = mschapv2_id; + if (data->prev_error) { + /* + * TODO: this does not seem to be enough when processing two + * or more failure messages. IAS did not increment mschapv2_id + * in its own packets, but it seemed to expect the peer to + * increment this for all packets(?). + */ + ms->mschapv2_id++; + } + WPA_PUT_BE16(ms->ms_length, ms_len); + + wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ + + /* Response */ + r = wpabuf_put(resp, sizeof(*r)); + peer_challenge = r->peer_challenge; + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " + "in Phase 1"); + peer_challenge = data->peer_challenge; + os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); + } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) { + wpabuf_free(resp); + return NULL; + } + os_memset(r->reserved, 0, 8); + if (data->auth_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " + "in Phase 1"); + auth_challenge = data->auth_challenge; + } + mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, auth_challenge, + peer_challenge, r->nt_response, + data->auth_response, data->master_key); + data->auth_response_valid = 1; + data->master_key_valid = 1; + + r->flags = 0; /* reserved, must be zero */ + + wpabuf_put_data(resp, identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(response)", id, ms->mschapv2_id); + return resp; +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in the request + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_challenge( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + size_t len, challenge_len; + const u8 *pos, *challenge; + + if (eap_get_config_identity(sm, &len) == NULL || + eap_get_config_password(sm, &len) == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); + if (req_len < sizeof(*req) + 1) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " + "(len %lu)", (unsigned long) req_len); + ret->ignore = TRUE; + return NULL; + } + pos = (const u8 *) (req + 1); + challenge_len = *pos++; + len = req_len - sizeof(*req) - 1; + if (challenge_len != MSCHAPV2_CHAL_LEN) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " + "%lu", (unsigned long) challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (len < challenge_len) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" + " packet: len=%lu challenge_len=%lu", + (unsigned long) len, (unsigned long) challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (data->passwd_change_challenge_valid) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " + "failure message"); + challenge = data->passwd_change_challenge; + } else + challenge = pos; + pos += challenge_len; + len -= challenge_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", + pos, len); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, + challenge); +} + + +static void eap_mschapv2_password_changed(struct eap_sm *sm, + struct eap_mschapv2_data *data) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) { + wpa_msg(sm->msg_ctx, MSG_INFO, + WPA_EVENT_PASSWORD_CHANGED + "EAP-MSCHAPV2: Password changed successfully"); + data->prev_error = 0; + os_free(config->password); + if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + config->password = os_malloc(16); + config->password_len = 16; + if (config->password) { + nt_password_hash(config->new_password, + config->new_password_len, + config->password); + } + os_free(config->new_password); + } else { + config->password = config->new_password; + config->password_len = config->new_password_len; + } + config->new_password = NULL; + config->new_password_len = 0; + } +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in th erequest + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *pos; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); + len = req_len - sizeof(*req); + pos = (const u8 *) (req + 1); + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, pos, len)) { + wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " + "response in success request"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + while (len > 0 && *pos == ' ') { + pos++; + len--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", + pos, len); + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); + + /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success + * message. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " + "buffer for success response"); + ret->ignore = TRUE; + return NULL; + } + + wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + data->success = 1; + + if (data->prev_error == ERROR_PASSWD_EXPIRED) + eap_mschapv2_password_changed(sm, data); + + return resp; +} + + +static int eap_mschapv2_failure_txt(struct eap_sm *sm, + struct eap_mschapv2_data *data, char *txt) +{ + char *pos, *msg = ""; + int retry = 1; + struct eap_peer_config *config = eap_get_config(sm); + + /* For example: + * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure + */ + + pos = txt; + + if (pos && os_strncmp(pos, "E=", 2) == 0) { + pos += 2; + data->prev_error = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", + data->prev_error); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "R=", 2) == 0) { + pos += 2; + retry = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", + retry == 1 ? "" : "not "); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "C=", 2) == 0) { + int hex_len; + pos += 2; + hex_len = os_strchr(pos, ' ') - (char *) pos; + if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { + if (hexstr2bin(pos, data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " + "failure challenge"); + } else { + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " + "challenge", + data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN); + data->passwd_change_challenge_valid = 1; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " + "challenge len %d", hex_len); + } + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " + "was not present in failure message"); + } + + if (pos && os_strncmp(pos, "V=", 2) == 0) { + pos += 2; + data->passwd_change_version = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " + "protocol version %d", data->passwd_change_version); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "M=", 2) == 0) { + pos += 2; + msg = pos; + } + wpa_msg(sm->msg_ctx, MSG_WARNING, + "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " + "%d)", + msg, retry == 1 ? "" : "not ", data->prev_error); + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3 && config) { + if (config->new_password == NULL) { + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP-MSCHAPV2: Password expired - password " + "change required"); + eap_sm_request_new_password(sm); + } + } else if (retry == 1 && config) { + /* TODO: could prevent the current password from being used + * again at least for some period of time */ + if (!config->mschapv2_retry) + eap_sm_request_identity(sm); + eap_sm_request_password(sm); + config->mschapv2_retry = 1; + } else if (config) { + /* TODO: prevent retries using same username/password */ + config->mschapv2_retry = 0; + } + + return retry == 1; +} + + +static struct wpabuf * eap_mschapv2_change_password( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) +{ + struct wpabuf *resp; + int ms_len; + const u8 *username, *password, *new_password; + size_t username_len, password_len, new_password_len; + struct eap_mschapv2_hdr *ms; + struct ms_change_password *cp; + u8 password_hash[16], password_hash_hash[16]; + int pwhash; + + username = eap_get_config_identity(sm, &username_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + new_password = eap_get_config_new_password(sm, &new_password_len); + if (username == NULL || password == NULL || new_password == NULL) + return NULL; + + username = mschapv2_remove_domain(username, &username_len); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + ms_len = sizeof(*ms) + sizeof(*cp); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; + ms->mschapv2_id = req->mschapv2_id + 1; + WPA_PUT_BE16(ms->ms_length, ms_len); + cp = wpabuf_put(resp, sizeof(*cp)); + + /* Encrypted-Password */ + if (pwhash) { + if (encrypt_pw_block_with_password_hash( + new_password, new_password_len, + password, cp->encr_password)) + goto fail; + } else { + if (new_password_encrypted_with_old_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_password)) + goto fail; + } + + /* Encrypted-Hash */ + if (pwhash) { + u8 new_password_hash[16]; + nt_password_hash(new_password, new_password_len, + new_password_hash); + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash); + } else { + old_nt_password_hash_encrypted_with_new_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_hash); + } + + /* Peer-Challenge */ + if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + goto fail; + + /* Reserved, must be zero */ + os_memset(cp->reserved, 0, 8); + + /* NT-Response */ + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", + data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", + cp->peer_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", + new_password, new_password_len); + generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, + username, username_len, + new_password, new_password_len, + cp->nt_response); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", + cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); + + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + generate_authenticator_response(new_password, new_password_len, + cp->peer_challenge, + data->passwd_change_challenge, + username, username_len, + cp->nt_response, data->auth_response); + data->auth_response_valid = 1; + + /* Likewise, generate master_key here since we have the needed data + * available. */ + nt_password_hash(new_password, new_password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + get_master_key(password_hash_hash, cp->nt_response, data->master_key); + data->master_key_valid = 1; + + /* Flags */ + os_memset(cp->flags, 0, 2); + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(change pw)", id, ms->mschapv2_id); + + return resp; + +fail: + wpabuf_free(resp); + return NULL; +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in th erequest + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *msdata = (const u8 *) (req + 1); + char *buf; + size_t len = req_len - sizeof(*req); + int retry = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", + msdata, len); + /* + * eap_mschapv2_failure_txt() expects a nul terminated string, so we + * must allocate a large enough temporary buffer to create that since + * the received message does not include nul termination. + */ + buf = os_malloc(len + 1); + if (buf) { + os_memcpy(buf, msdata, len); + buf[len] = '\0'; + retry = eap_mschapv2_failure_txt(sm, data, buf); + os_free(buf); + } + + ret->ignore = FALSE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3) { + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) + return eap_mschapv2_change_password(sm, data, ret, req, + id); + if (config && config->pending_req_new_password) + return NULL; + } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + /* TODO: could try to retry authentication, e.g, after having + * changed the username/password. In this case, EAP MS-CHAP-v2 + * Failure Response would not be sent here. */ + return NULL; + } + + /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure + * message. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ + + return resp; +} + + +static int eap_mschapv2_check_config(struct eap_sm *sm) +{ + size_t len; + + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); + eap_sm_request_identity(sm); + return -1; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + eap_sm_request_password(sm); + return -1; + } + + return 0; +} + + +static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, + const struct eap_mschapv2_hdr *ms) +{ + size_t ms_len = WPA_GET_BE16(ms->ms_length); + + if (ms_len == len) + return 0; + + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " + "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); + if (sm->workaround) { + /* Some authentication servers use invalid ms_len, + * ignore it for interoperability. */ + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" + " invalid ms_len %lu (len %lu)", + (unsigned long) ms_len, + (unsigned long) len); + return 0; + } + + return -1; +} + + +static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, + const struct wpabuf *reqData) +{ + /* + * Store a copy of the challenge message, so that it can be processed + * again in case retry is allowed after a possible failure. + */ + wpabuf_free(data->prev_challenge); + data->prev_challenge = wpabuf_dup(reqData); +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_mschapv2_data *data = priv; + struct eap_peer_config *config = eap_get_config(sm); + const struct eap_mschapv2_hdr *ms; + int using_prev_challenge = 0; + const u8 *pos; + size_t len; + u8 id; + + if (eap_mschapv2_check_config(sm)) { + ret->ignore = TRUE; + return NULL; + } + + if (config->mschapv2_retry && data->prev_challenge && + data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " + "with the previous challenge"); + + reqData = data->prev_challenge; + using_prev_challenge = 1; + config->mschapv2_retry = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, + &len); + if (pos == NULL || len < sizeof(*ms) + 1) { + ret->ignore = TRUE; + return NULL; + } + + ms = (const struct eap_mschapv2_hdr *) pos; + if (eap_mschapv2_check_mslen(sm, len, ms)) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", + id, ms->mschapv2_id); + + switch (ms->op_code) { + case MSCHAPV2_OP_CHALLENGE: + if (!using_prev_challenge) + eap_mschapv2_copy_challenge(data, reqData); + return eap_mschapv2_challenge(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, ms, len, id); + default: + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", + ms->op_code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + key_len = 2 * MSCHAPV2_KEY_LEN; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., + * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0); + + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", + key, key_len); + + *len = key_len; + return key; +} + + +/** + * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method + * Returns: 0 on success, -1 on failure + * + * This function is used to register EAP-MSCHAPv2 peer method into the EAP + * method list. + */ +int eap_peer_mschapv2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->deinit = eap_mschapv2_deinit; + eap->process = eap_mschapv2_process; + eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; + eap->getKey = eap_mschapv2_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c new file mode 100644 index 0000000000000..556c22f9e2d59 --- /dev/null +++ b/src/eap_peer/eap_otp.c @@ -0,0 +1,107 @@ +/* + * EAP peer method: EAP-OTP (RFC 3748) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" + + +static void * eap_otp_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_otp_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *password; + size_t password_len, len; + int otp; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", + pos, len); + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", + password, password_len); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_otp_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); + if (eap == NULL) + return -1; + + eap->init = eap_otp_init; + eap->deinit = eap_otp_deinit; + eap->process = eap_otp_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c new file mode 100644 index 0000000000000..afd56dd49b7fa --- /dev/null +++ b/src/eap_peer/eap_pax.c @@ -0,0 +1,532 @@ +/* + * EAP peer method: EAP-PAX (RFC 4746) + * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_pax_common.h" +#include "sha1.h" +#include "crypto.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + * + * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite + * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and + * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), + * RSAES-OAEP). + */ + +struct eap_pax_data { + enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; + u8 mac_id, dh_group_id, public_key_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + char *cid; + size_t cid_len; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; +}; + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct eap_pax_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (!identity || !password) { + wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " + "not configured"); + return NULL; + } + + if (password_len != EAP_PAX_AK_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = PAX_INIT; + + data->cid = os_malloc(identity_len); + if (data->cid == NULL) { + eap_pax_deinit(sm, data); + return NULL; + } + os_memcpy(data->cid, identity, identity_len); + data->cid_len = identity_len; + + os_memcpy(data->ak, password, EAP_PAX_AK_LEN); + + return data; +} + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + os_free(data->cid); + os_free(data); +} + + +static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, + u8 id, u8 op_code, size_t plen) +{ + struct wpabuf *resp; + struct eap_pax_hdr *pax; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + pax = wpabuf_put(resp, sizeof(*pax)); + pax->op_code = op_code; + pax->flags = 0; + pax->mac_id = req->mac_id; + pax->dh_group_id = req->dh_group_id; + pax->public_key_id = req->public_key_id; + + return resp; +} + + +static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, + struct eap_method_ret *ret, u8 id, + const struct eap_pax_hdr *req, + size_t req_plen) +{ + struct wpabuf *resp; + const u8 *pos; + u8 *rpos; + size_t left, plen; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); + + if (data->state != PAX_INIT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = req_plen - sizeof(*req); + + if (left < 2 + EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " + "length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); + ret->ignore = TRUE; + return NULL; + } + + pos += 2; + left -= 2; + os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", + data->rand.r.x, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + + if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, + data->mk, data->ck, data->ick) < 0) + { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); + + plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + + EAP_PAX_ICV_LEN; + resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); + if (resp == NULL) + return NULL; + + wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); + wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + + wpabuf_put_be16(resp, data->cid_len); + wpabuf_put_data(resp, data->cid, data->cid_len); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", + (u8 *) data->cid, data->cid_len); + + wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); + rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); + eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + rpos, EAP_PAX_MAC_LEN); + + /* Optional ADE could be added here, if needed */ + + rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + + data->state = PAX_STD_2_SENT; + data->mac_id = req->mac_id; + data->dh_group_id = req->dh_group_id; + data->public_key_id = req->public_key_id; + + return resp; +} + + +static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, + struct eap_method_ret *ret, u8 id, + const struct eap_pax_hdr *req, + size_t req_plen) +{ + struct wpabuf *resp; + u8 *rpos, mac[EAP_PAX_MAC_LEN]; + const u8 *pos; + size_t left; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); + + if (data->state != PAX_STD_2_SENT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = req_plen - sizeof(*req); + + if (left < 2 + EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " + "MAC_CK length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); + ret->ignore = TRUE; + return NULL; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, mac); + if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " + "received"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", + mac, EAP_PAX_MAC_LEN); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); + + resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); + if (resp == NULL) + return NULL; + + /* Optional ADE could be added here, if needed */ + + rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + + data->state = PAX_DONE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_pax_data *data = priv; + const struct eap_pax_hdr *req; + struct wpabuf *resp; + u8 icvbuf[EAP_PAX_ICV_LEN], id; + const u8 *icv, *pos; + size_t len; + u16 flen, mlen; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); + if (pos == NULL || len < EAP_PAX_ICV_LEN) { + ret->ignore = TRUE; + return NULL; + } + id = eap_get_id(reqData); + req = (const struct eap_pax_hdr *) pos; + flen = len - EAP_PAX_ICV_LEN; + mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + req->op_code, req->flags, req->mac_id, req->dh_group_id, + req->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + pos, len - EAP_PAX_ICV_LEN); + + if (data->state != PAX_INIT && data->mac_id != req->mac_id) { + wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->mac_id, req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { + wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->dh_group_id, req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && + data->public_key_id != req->public_key_id) { + wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->public_key_id, req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ + if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", + req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", + req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", + req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " + "ignored packet"); + ret->ignore = TRUE; + return NULL; + } + + icv = pos + len - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + if (req->op_code == EAP_PAX_OP_STD_1) { + eap_pax_mac(req->mac_id, (u8 *) "", 0, + wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, + icvbuf); + } else { + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, + icvbuf); + } + if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " + "message"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (req->op_code) { + case EAP_PAX_OP_STD_1: + resp = eap_pax_process_std_1(data, ret, id, req, flen); + break; + case EAP_PAX_OP_STD_3: + resp = eap_pax_process_std_3(data, ret, id, req, flen); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " + "op_code %d", req->op_code); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == PAX_DONE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != PAX_DONE) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_MSK_LEN, key); + + return key; +} + + +static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != PAX_DONE) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Extended Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_EMSK_LEN, key); + + return key; +} + + +int eap_peer_pax_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); + if (eap == NULL) + return -1; + + eap->init = eap_pax_init; + eap->deinit = eap_pax_deinit; + eap->process = eap_pax_process; + eap->isKeyAvailable = eap_pax_isKeyAvailable; + eap->getKey = eap_pax_getKey; + eap->get_emsk = eap_pax_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c new file mode 100644 index 0000000000000..894fc632fda36 --- /dev/null +++ b/src/eap_peer/eap_peap.c @@ -0,0 +1,1288 @@ +/* + * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "tls.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_peap_common.h" +#include "tncc.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_eap_success; + int phase2_eap_started; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + + struct wpabuf *pending_phase2_req; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + int crypto_binding_used; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) + * is enabled. */ +}; + + +static int eap_peap_parse_phase1(struct eap_peap_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "peapver="); + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", + data->force_peap_version); + } + + if (os_strstr(phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " + "derivation"); + } + + if (os_strstr(phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " + "tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " + "after receiving tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " + "receiving tunneled EAP-Success"); + } + + if (os_strstr(phase1, "crypto_binding=0")) { + data->crypto_binding = NO_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=1")) { + data->crypto_binding = OPTIONAL_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=2")) { + data->crypto_binding = REQUIRE_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); + } + +#ifdef EAP_TNC + if (os_strstr(phase1, "tnc=soh2")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } else if (os_strstr(phase1, "tnc=soh1")) { + data->soh = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); + } else if (os_strstr(phase1, "tnc=soh")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } +#endif /* EAP_TNC */ + + return 0; +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + sm->peap_done = FALSE; + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + data->crypto_binding = OPTIONAL_BINDING; + + if (config && config->phase1 && + eap_peap_parse_phase1(data, config->phase1) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + os_free(data->phase2_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +/** + * eap_tlv_build_nak - Build EAP-TLV NAK message + * @id: EAP identifier for the header + * @nak_type: TLV type (EAP_TLV_*) + * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure + * + * This funtion builds an EAP-TLV NAK message. The caller is responsible for + * freeing the returned buffer. + */ +static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); + wpabuf_put_be16(msg, 6); /* Length */ + wpabuf_put_be32(msg, 0); /* Vendor-Id */ + wpabuf_put_be16(msg, nak_type); /* NAK-Type */ + + return msg; +} + + +static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_method == NULL || data->phase2_priv == NULL || + data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = data->key_data; + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + return 0; + } + + if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static int eap_tlv_add_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *buf) +{ + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ + wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); + data->crypto_binding_used = 1; + + return 0; +} + + +/** + * eap_tlv_build_result - Build EAP-TLV Result message + * @id: EAP identifier for the header + * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) + * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure + * + * This funtion builds an EAP-TLV Result message. The caller is responsible for + * freeing the returned buffer. + */ +static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, + struct eap_peap_data *data, + int crypto_tlv_used, + int id, u16 status) +{ + struct wpabuf *msg; + size_t len; + + if (data->crypto_binding == NO_BINDING) + crypto_tlv_used = 0; + + len = 6; + if (crypto_tlv_used) + len += 60; /* Cryptobinding TLV */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); + wpabuf_put_be16(msg, 2); /* Length */ + wpabuf_put_be16(msg, status); /* Status */ + + if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (eap_peap_derive_cmk(sm, data) < 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); + return -1; + } + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + os_memcpy(data->binding_nonce, pos, 32); + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", + buf, sizeof(buf)); + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC", + pos, SHA1_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC", + mac, SHA1_MAC_LEN); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + +/** + * eap_tlv_process - Process a received EAP-TLV message and generate a response + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ret: Return values from EAP request validation and processing + * @req: EAP-TLV request to be processed. The caller must have validated that + * the buffer is large enough to contain full request (hdr->length bytes) and + * that the EAP type is EAP_TYPE_TLV. + * @resp: Buffer to return a pointer to the allocated response message. This + * field should be initialized to %NULL before the call. The value will be + * updated if a response message is generated. The caller is responsible for + * freeing the allocated message. + * @force_failure: Force negotiation to fail + * Returns: 0 on success, -1 on failure + */ +static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, + struct eap_method_ret *ret, + const struct wpabuf *req, struct wpabuf **resp, + int force_failure) +{ + size_t left, tlv_len; + const u8 *pos; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; + int tlv_type, mandatory; + + /* Parse TLVs */ + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); + if (pos == NULL) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + tlv_len = WPA_GET_BE16(pos); + pos += 2; + left -= 4; + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%lu left=%lu)", + (unsigned long) tlv_len, + (unsigned long) left); + return -1; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + /* NAK TLV and ignore all TLVs in this packet. + */ + *resp = eap_tlv_build_nak(eap_get_id(req), + tlv_type); + return *resp == NULL ? -1 : 0; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + return -1; + } + + /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding != NO_BINDING) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + if (result_tlv == NULL) + return -1; + force_failure = 1; + crypto_tlv = NULL; /* do not include Cryptobinding TLV + * in response, if the received + * cryptobinding was invalid. */ + } + } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + return -1; + } + + if (result_tlv) { + int status, resp_status; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + return -1; + } + status = WPA_GET_BE16(result_tlv); + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- EAP-TLV/Phase2 Completed"); + if (force_failure) { + wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" + " - force failed Phase 2"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + resp_status = EAP_TLV_RESULT_SUCCESS; + ret->decision = DECISION_UNCOND_SUCC; + } + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } + ret->methodState = METHOD_DONE; + + *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, + eap_get_id(req), resp_status); + } + + return 0; +} + + +static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + struct eap_tlv_hdr *tlv; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + tlv = wpabuf_put(e, sizeof(*tlv)); + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(wpabuf_len(buf)); + wpabuf_put_buf(e, buf); + wpabuf_free(buf); + return e; +} + + +static int eap_peap_phase2_request(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct wpabuf *req, + struct wpabuf **resp) +{ + struct eap_hdr *hdr = wpabuf_mhead(req); + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + break; + case EAP_TYPE_TLV: + os_memset(&iret, 0, sizeof(iret)); + if (eap_tlv_process(sm, data, &iret, req, resp, + data->phase2_eap_started && + !data->phase2_eap_success)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + if (iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + data->phase2_success = 1; + } + break; + case EAP_TYPE_EXPANDED: +#ifdef EAP_TNC + if (data->soh) { + const u8 *epos; + size_t eleft; + + epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, + req, &eleft); + if (epos) { + struct wpabuf *buf; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: SoH EAP Extensions"); + buf = tncc_process_soh_request(data->soh, + epos, eleft); + if (buf) { + *resp = eap_msg_alloc( + EAP_VENDOR_MICROSOFT, 0x21, + wpabuf_len(buf), + EAP_CODE_RESPONSE, + hdr->identifier); + if (*resp == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + wpabuf_put_buf(*resp, buf); + wpabuf_free(buf); + break; + } + } + } +#endif /* EAP_TNC */ + /* fall through */ + default: + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE) { + size_t i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != + EAP_VENDOR_IETF || + data->phase2_types[i].method != *pos) + continue; + + data->phase2_type.vendor = + data->phase2_types[i].vendor; + data->phase2_type.method = + data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " + "Phase 2 EAP vendor %d method %d", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + } + if (*pos != data->phase2_type.method || + *pos == EAP_TYPE_NONE) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + data->phase2_type.vendor, + data->phase2_type.method); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + data->phase2_eap_started = 1; + os_memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, req); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_eap_success = 1; + data->phase2_success = 1; + } + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } + + return 0; +} + + +static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted = NULL; + int res, skip_change = 0; + struct eap_hdr *hdr, *rhdr; + struct wpabuf *resp = NULL; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + skip_change = 1; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0 && sm->workaround && + data->phase2_success) { + /* + * Cisco ACS seems to be using TLS ACK to terminate + * EAP-PEAPv0/GTC. Try to reply with TLS ACK. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " + "expected data - acknowledge with TLS ACK since " + "Phase 2 has been completed"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_DONE; + return 1; + } else if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, + req->identifier, NULL, out_data); + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (res) + return res; + +continue_req: + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted); + + hdr = wpabuf_mhead(in_decrypted); + if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && + be_to_host16(hdr->length) == 5 && + eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { + /* At least FreeRADIUS seems to send full EAP header with + * EAP Request Identity */ + skip_change = 1; + } + if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && + eap_get_type(in_decrypted) == EAP_TYPE_TLV) { + skip_change = 1; + } + + if (data->peap_version == 0 && !skip_change) { + struct eap_hdr *nhdr; + struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); + wpabuf_put_buf(nmsg, in_decrypted); + nhdr->code = req->code; + nhdr->identifier = req->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + if (data->peap_version >= 2) { + struct eap_tlv_hdr *tlv; + struct wpabuf *nmsg; + + if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " + "EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + tlv = wpabuf_mhead(in_decrypted); + if ((be_to_host16(tlv->tlv_type) & 0x3fff) != + EAP_TLV_EAP_PAYLOAD_TLV) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + if (sizeof(*tlv) + be_to_host16(tlv->length) > + wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " + "length"); + wpabuf_free(in_decrypted); + return 0; + } + hdr = (struct eap_hdr *) (tlv + 1); + if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " + "EAP packet in EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + + nmsg = wpabuf_alloc(be_to_host16(hdr->length)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + + wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + hdr = wpabuf_mhead(in_decrypted); + if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return 0; + } + len = be_to_host16(hdr->length); + if (len > wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) wpabuf_len(in_decrypted), + (unsigned long) len); + wpabuf_free(in_decrypted); + return 0; + } + if (len < wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " + "shorter length than full decrypted data " + "(%lu < %lu)", + (unsigned long) len, + (unsigned long) wpabuf_len(in_decrypted)); + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_peap_phase2_request(sm, data, ret, in_decrypted, + &resp)) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " + "processing failed"); + return 0; + } + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->peap_version == 1) { + /* EAP-Success within TLS tunnel is used to indicate + * shutdown of the TLS channel. The authentication has + * been completed. */ + if (data->phase2_eap_started && + !data->phase2_eap_success) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " + "Success used to indicate success, " + "but Phase 2 EAP was not yet " + "completed successfully"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(in_decrypted); + return 0; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " + "EAP-Success within TLS tunnel - " + "authentication completed"); + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + data->phase2_success = 1; + if (data->peap_outer_success == 2) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " + "to finish authentication"); + return 1; + } else if (data->peap_outer_success == 1) { + /* Reply with EAP-Success within the TLS + * channel to complete the authentication. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_SUCCESS; + rhdr->identifier = hdr->identifier; + rhdr->length = + host_to_be16(sizeof(*rhdr)); + } + } else { + /* No EAP-Success expected for Phase 1 (outer, + * unencrypted auth), so force EAP state + * machine to SUCCESS state. */ + sm->peap_done = TRUE; + } + } else { + /* FIX: ? */ + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_MAY_CONT; + ret->allowNotifications = FALSE; + /* Reply with EAP-Failure within the TLS channel to complete + * failure reporting. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_FAILURE; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(sizeof(*rhdr)); + } + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + wpabuf_free(in_decrypted); + + if (resp) { + int skip_change2 = 0; + struct wpabuf *rmsg, buf; + + wpa_hexdump_buf_key(MSG_DEBUG, + "EAP-PEAP: Encrypting Phase 2 data", resp); + /* PEAP version changes */ + if (data->peap_version >= 2) { + resp = eap_peapv2_tlv_eap_payload(resp); + if (resp == NULL) + return -1; + } + if (wpabuf_len(resp) >= 5 && + wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && + eap_get_type(resp) == EAP_TYPE_TLV) + skip_change2 = 1; + rmsg = resp; + if (data->peap_version == 0 && !skip_change2) { + wpabuf_set(&buf, wpabuf_head_u8(resp) + + sizeof(struct eap_hdr), + wpabuf_len(resp) - sizeof(struct eap_hdr)); + rmsg = &buf; + } + + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, req->identifier, + rmsg, out_data)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " + "a Phase 2 frame"); + } + wpabuf_free(resp); + } + + return 0; +} + + +static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_peap_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " + "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + data->peap_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) + data->peap_version = flags & EAP_PEAP_VERSION_MASK; + if (data->force_peap_version >= 0 && + data->force_peap_version != data->peap_version) { + wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " + "forced PEAP version %d", + data->force_peap_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", + data->peap_version); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); + } else { + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_PEAP, + data->peap_version, id, pos, + left, &resp); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char *label; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS done, proceed to Phase 2"); + os_free(data->key_data); + /* draft-josefsson-ppext-eap-tls-eap-05.txt + * specifies that PEAPv1 would use "client PEAP + * encryption" as the label. However, most existing + * PEAPv1 implementations seem to be using the old + * label, "client EAP encryption", instead. Use the old + * label by default, but allow it to be configured with + * phase1 parameter peaplabel=1. */ + if (data->peap_version > 1 || data->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_peer_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + + if (sm->workaround && data->resuming) { + /* + * At least few RADIUS servers (Aegis v1.1.6; + * but not v1.1.4; and Cisco ACS) seem to be + * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco + * ACS) session resumption with outer + * EAP-Success. This does not seem to follow + * draft-josefsson-pppext-eap-tls-eap-05.txt + * section 4.2, so only allow this if EAP + * workarounds are enabled. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " + "allow outer EAP-Success to " + "terminate PEAP resumption"); + ret->decision = DECISION_COND_SUCC; + data->phase2_success = 1; + } + + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, + &resp); + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + return resp; +} + + +static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; + data->crypto_binding_used = 0; +} + + +static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_success = 0; + data->phase2_eap_success = 0; + data->phase2_eap_started = 0; + data->resuming = 1; + data->reauth = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_peap_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%s\n", + data->peap_version, + data->phase2_method->name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + if (data->crypto_binding_used) { + u8 csk[128]; + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + os_memcpy(key, csk, EAP_TLS_KEY_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + key, EAP_TLS_KEY_LEN); + } else + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +int eap_peer_peap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_peap_init; + eap->deinit = eap_peap_deinit; + eap->process = eap_peap_process; + eap->isKeyAvailable = eap_peap_isKeyAvailable; + eap->getKey = eap_peap_getKey; + eap->get_status = eap_peap_get_status; + eap->has_reauth_data = eap_peap_has_reauth_data; + eap->deinit_for_reauth = eap_peap_deinit_for_reauth; + eap->init_for_reauth = eap_peap_init_for_reauth; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c new file mode 100644 index 0000000000000..1ce635663982f --- /dev/null +++ b/src/eap_peer/eap_psk.c @@ -0,0 +1,482 @@ +/* + * EAP peer method: EAP-PSK (RFC 4764) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Note: EAP-PSK is an EAP authentication method and as such, completely + * different from WPA-PSK. This file is not needed for WPA-PSK functionality. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "aes_wrap.h" +#include "eap_common/eap_psk_common.h" + + +struct eap_psk_data { + enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; + u8 *id_s, *id_p; + size_t id_s_len, id_p_len; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password || password_len != 16) { + wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + if (eap_psk_key_setup(password, data->ak, data->kdk)) { + os_free(data); + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + data->state = PSK_INIT; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->id_p = os_malloc(identity_len); + if (data->id_p) + os_memcpy(data->id_p, identity, identity_len); + data->id_p_len = identity_len; + } + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_psk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + os_free(data->id_s); + os_free(data->id_p); + os_free(data); +} + + +static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_1 *hdr1; + struct eap_psk_hdr_2 *hdr2; + struct wpabuf *resp; + u8 *buf, *pos; + size_t buflen, len; + const u8 *cpos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); + + cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + hdr1 = (const struct eap_psk_hdr_1 *) cpos; + if (cpos == NULL || len < sizeof(*hdr1)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr1)); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); + if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", + EAP_PSK_FLAGS_GET_T(hdr1->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, + EAP_PSK_RAND_LEN); + os_free(data->id_s); + data->id_s_len = len - sizeof(*hdr1); + data->id_s = os_malloc(data->id_s_len); + if (data->id_s == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " + "ID_S (len=%lu)", (unsigned long) data->id_s_len); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", + data->id_s, data->id_s_len); + + if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, + eap_get_id(reqData)); + if (resp == NULL) + return NULL; + hdr2 = wpabuf_put(resp, sizeof(*hdr2)); + hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ + os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); + wpabuf_put_data(resp, data->id_p, data->id_p_len); + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) { + wpabuf_free(resp); + return NULL; + } + os_memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + os_memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { + os_free(buf); + wpabuf_free(resp); + return NULL; + } + os_free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + data->state = PSK_MAC_SENT; + + return resp; +} + + +static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_3 *hdr3; + struct eap_psk_hdr_4 *hdr4; + struct wpabuf *resp; + u8 *buf, *rpchannel, nonce[16], *decrypted; + const u8 *pchannel, *tag, *msg; + u8 mac[EAP_PSK_MAC_LEN]; + size_t buflen, left, data_len, len, plen; + int failed = 0; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, + reqData, &len); + hdr3 = (const struct eap_psk_hdr_3 *) pos; + if (pos == NULL || len < sizeof(*hdr3)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr3)); + ret->ignore = TRUE; + return NULL; + } + left = len - sizeof(*hdr3); + pchannel = (const u8 *) (hdr3 + 1); + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); + if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", + EAP_PSK_FLAGS_GET_T(hdr3->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "third message (len=%lu, expected 21)", + (unsigned long) left); + ret->ignore = TRUE; + return NULL; + } + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) + return NULL; + os_memcpy(buf, data->id_s, data->id_s_len); + os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, mac)) { + os_free(buf); + return NULL; + } + os_free(buf); + if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " + "message"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); + + if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, + data->msk, data->emsk)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); + + os_memset(nonce, 0, 12); + os_memcpy(nonce + 12, pchannel, 4); + pchannel += 4; + left -= 4; + + tag = pchannel; + pchannel += 16; + left -= 16; + + msg = pchannel; + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", + nonce, sizeof(nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", + wpabuf_head(reqData), 5); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); + + decrypted = os_malloc(left); + if (decrypted == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + os_memcpy(decrypted, msg, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(reqData), + sizeof(struct eap_hdr) + 1 + + sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, + left, tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + os_free(decrypted); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + failed = 1; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " + "authentication"); + failed = 1; + break; + } + + data_len = 1; + if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) + data_len++; + plen = sizeof(*hdr4) + 4 + 16 + data_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + os_free(decrypted); + return NULL; + } + hdr4 = wpabuf_put(resp, sizeof(*hdr4)); + hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ + os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); + rpchannel = wpabuf_put(resp, 4 + 16 + data_len); + + /* nonce++ */ + inc_byte_array(nonce, sizeof(nonce)); + os_memcpy(rpchannel, nonce + 12, 4); + + if (decrypted[0] & EAP_PSK_E_FLAG) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); + failed = 1; + rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | + EAP_PSK_E_FLAG; + if (left > 1) { + /* Add empty EXT_Payload with same EXT_Type */ + rpchannel[4 + 16 + 1] = decrypted[1]; + } + } else if (failed) + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; + else + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", + rpchannel + 4 + 16, data_len); + if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(resp), + sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), + rpchannel + 4 + 16, data_len, rpchannel + 4)) { + os_free(decrypted); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", + rpchannel, 4 + 16 + data_len); + + wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", + failed ? "un" : ""); + data->state = PSK_DONE; + ret->methodState = METHOD_DONE; + ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + + os_free(decrypted); + + return resp; +} + + +static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_psk_data *data = priv; + const u8 *pos; + struct wpabuf *resp = NULL; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (data->state) { + case PSK_INIT: + resp = eap_psk_process_1(data, ret, reqData); + break; + case PSK_MAC_SENT: + resp = eap_psk_process_3(data, ret, reqData); + break; + case PSK_DONE: + wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " + "unexpected message"); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == PSK_DONE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + os_memcpy(key, data->msk, EAP_MSK_LEN); + + return key; +} + + +static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_psk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); + if (eap == NULL) + return -1; + + eap->init = eap_psk_init; + eap->deinit = eap_psk_deinit; + eap->process = eap_psk_process; + eap->isKeyAvailable = eap_psk_isKeyAvailable; + eap->getKey = eap_psk_getKey; + eap->get_emsk = eap_psk_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c new file mode 100644 index 0000000000000..bb06bb2f4256e --- /dev/null +++ b/src/eap_peer/eap_sake.c @@ -0,0 +1,499 @@ +/* + * EAP peer method: EAP-SAKE (RFC 4763) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_sake_common.h" + +struct eap_sake_data { + enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; + u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN]; + u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN]; + u8 rand_s[EAP_SAKE_RAND_LEN]; + u8 rand_p[EAP_SAKE_RAND_LEN]; + struct { + u8 auth[EAP_SAKE_TEK_AUTH_LEN]; + u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; + } tek; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 session_id; + int session_id_set; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; +}; + + +static const char * eap_sake_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case CHALLENGE: + return "CHALLENGE"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_sake_state(struct eap_sake_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", + eap_sake_state_txt(data->state), + eap_sake_state_txt(state)); + data->state = state; +} + + +static void eap_sake_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_sake_init(struct eap_sm *sm) +{ + struct eap_sake_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { + wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = IDENTITY; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_sake_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN); + os_memcpy(data->root_secret_b, + password + EAP_SAKE_ROOT_SECRET_LEN, + EAP_SAKE_ROOT_SECRET_LEN); + + return data; +} + + +static void eap_sake_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + os_free(data->serverid); + os_free(data->peerid); + os_free(data); +} + + +static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, + int id, size_t length, u8 subtype) +{ + struct eap_sake_hdr *sake; + struct wpabuf *msg; + size_t plen; + + plen = length + sizeof(struct eap_sake_hdr); + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " + "request"); + return NULL; + } + + sake = wpabuf_put(msg, sizeof(*sake)); + sake->version = EAP_SAKE_VERSION; + sake->session_id = data->session_id; + sake->subtype = subtype; + + return msg; +} + + +static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + struct wpabuf *resp; + + if (data->state != IDENTITY) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.perm_id_req && !attr.any_id_req) { + wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or " + "AT_ANY_ID_REQ in Request/Identity"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); + + resp = eap_sake_build_msg(data, eap_get_id(reqData), + 2 + data->peerid_len, + EAP_SAKE_SUBTYPE_IDENTITY); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); + eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, + data->peerid, data->peerid_len); + + eap_sake_state(data, CHALLENGE); + + return resp; +} + + +static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + struct wpabuf *resp; + u8 *rpos; + size_t rlen; + + if (data->state != IDENTITY && data->state != CHALLENGE) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received " + "in unexpected state (%d)", data->state); + ret->ignore = TRUE; + return NULL; + } + if (data->state == IDENTITY) + eap_sake_state(data, CHALLENGE); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.rand_s) { + wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not " + "include AT_RAND_S"); + return NULL; + } + + os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", + data->rand_s, EAP_SAKE_RAND_LEN); + + if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)", + data->rand_p, EAP_SAKE_RAND_LEN); + + os_free(data->serverid); + data->serverid = NULL; + data->serverid_len = 0; + if (attr.serverid) { + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", + attr.serverid, attr.serverid_len); + data->serverid = os_malloc(attr.serverid_len); + if (data->serverid == NULL) + return NULL; + os_memcpy(data->serverid, attr.serverid, attr.serverid_len); + data->serverid_len = attr.serverid_len; + } + + eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, data->emsk); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); + + rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; + if (data->peerid) + rlen += 2 + data->peerid_len; + resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, + EAP_SAKE_SUBTYPE_CHALLENGE); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P"); + eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P, + data->rand_p, EAP_SAKE_RAND_LEN); + + if (data->peerid) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); + eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, + data->peerid, data->peerid_len); + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); + wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); + wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); + rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(resp), wpabuf_len(resp), rpos, + rpos)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + wpabuf_free(resp); + return NULL; + } + + eap_sake_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + u8 mic_s[EAP_SAKE_MIC_LEN]; + struct wpabuf *resp; + u8 *rpos; + + if (data->state != CONFIRM) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.mic_s) { + wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not " + "include AT_MIC_S"); + return NULL; + } + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 0, + wpabuf_head(reqData), wpabuf_len(reqData), + attr.mic_s, mic_s); + if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); + eap_sake_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " + "Response/Auth-Reject"); + return eap_sake_build_msg(data, eap_get_id(reqData), 0, + EAP_SAKE_SUBTYPE_AUTH_REJECT); + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); + + resp = eap_sake_build_msg(data, eap_get_id(reqData), + 2 + EAP_SAKE_MIC_LEN, + EAP_SAKE_SUBTYPE_CONFIRM); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); + wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); + wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); + rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(resp), wpabuf_len(resp), rpos, + rpos)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + wpabuf_free(resp); + return NULL; + } + + eap_sake_state(data, SUCCESS); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_sake_data *data = priv; + const struct eap_sake_hdr *req; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 subtype, session_id; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { + ret->ignore = TRUE; + return NULL; + } + + req = (const struct eap_sake_hdr *) pos; + end = pos + len; + subtype = req->subtype; + session_id = req->session_id; + pos = (const u8 *) (req + 1); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d " + "session_id %d", subtype, session_id); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", + pos, end - pos); + + if (data->session_id_set && data->session_id != session_id) { + wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", + session_id, data->session_id); + ret->ignore = TRUE; + return NULL; + } + data->session_id = session_id; + data->session_id_set = 1; + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (subtype) { + case EAP_SAKE_SUBTYPE_IDENTITY: + resp = eap_sake_process_identity(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CHALLENGE: + resp = eap_sake_process_challenge(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CONFIRM: + resp = eap_sake_process_confirm(sm, data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with " + "unknown subtype %d", subtype); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_sake_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); + if (eap == NULL) + return -1; + + eap->init = eap_sake_init; + eap->deinit = eap_sake_deinit; + eap->process = eap_sake_process; + eap->isKeyAvailable = eap_sake_isKeyAvailable; + eap->getKey = eap_sake_getKey; + eap->get_emsk = eap_sake_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c new file mode 100644 index 0000000000000..a7e49f898a222 --- /dev/null +++ b/src/eap_peer/eap_sim.c @@ -0,0 +1,1104 @@ +/* + * EAP peer method: EAP-SIM (RFC 4186) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_config.h" +#include "pcsc_funcs.h" +#include "eap_common/eap_sim_common.h" +#ifdef CONFIG_SIM_SIMULATOR +#include "hlr_auc_gw/milenage.h" +#endif /* CONFIG_SIM_SIMULATOR */ + + +struct eap_sim_data { + u8 *ver_list; + size_t ver_list_len; + int selected_version; + size_t min_num_chal, num_chal; + + u8 kc[3][EAP_SIM_KC_LEN]; + u8 sres[3][EAP_SIM_SRES_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[3][GSM_RAND_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { + CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + } state; + int result_ind, use_result_ind; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_sim_state_txt(int state) +{ + switch (state) { + case CONTINUE: + return "CONTINUE"; + case RESULT_SUCCESS: + return "RESULT_SUCCESS"; + case RESULT_FAILURE: + return "RESULT_FAILURE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_sim_state(struct eap_sim_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", + eap_sim_state_txt(data->state), + eap_sim_state_txt(state)); + data->state = state; +} + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + os_free(data); + return NULL; + } + + data->min_num_chal = 2; + if (config && config->phase1) { + char *pos = os_strstr(config->phase1, "sim_min_num_chal="); + if (pos) { + data->min_num_chal = atoi(pos + 17); + if (data->min_num_chal < 2 || data->min_num_chal > 3) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "sim_min_num_chal configuration " + "(%lu, expected 2 or 3)", + (unsigned long) data->min_num_chal); + os_free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " + "challenges to %lu", + (unsigned long) data->min_num_chal); + } + + data->result_ind = os_strstr(config->phase1, "result_ind=1") != + NULL; + } + + eap_sim_state(data, CONTINUE); + + return data; +} + + +static void eap_sim_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (data) { + os_free(data->ver_list); + os_free(data->pseudonym); + os_free(data->reauth_id); + os_free(data->last_eap_identity); + os_free(data); + } +} + + +static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) +{ + struct eap_peer_config *conf; + + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); + + conf = eap_get_config(sm); + if (conf == NULL) + return -1; + if (conf->pcsc) { + if (scard_gsm_auth(sm->scard_ctx, data->rand[0], + data->sres[0], data->kc[0]) || + scard_gsm_auth(sm->scard_ctx, data->rand[1], + data->sres[1], data->kc[1]) || + (data->num_chal > 2 && + scard_gsm_auth(sm->scard_ctx, data->rand[2], + data->sres[2], data->kc[2]))) { + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM " + "authentication could not be completed"); + return -1; + } + return 0; + } + +#ifdef CONFIG_SIM_SIMULATOR + if (conf->password) { + u8 opc[16], k[16]; + const char *pos; + size_t i; + wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage " + "implementation for authentication"); + if (conf->password_len < 65) { + wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage " + "password"); + return -1; + } + pos = (const char *) conf->password; + if (hexstr2bin(pos, k, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, opc, 16)) + return -1; + + for (i = 0; i < data->num_chal; i++) { + if (gsm_milenage(opc, k, data->rand[i], + data->sres[i], data->kc[i])) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "GSM-Milenage authentication " + "could not be completed"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + } + return 0; + } +#endif /* CONFIG_SIM_SIMULATOR */ + +#ifdef CONFIG_SIM_HARDCODED + /* These hardcoded Kc and SRES values are used for testing. RAND to + * KC/SREC mapping is very bogus as far as real authentication is + * concerned, but it is quite useful for cases where the AS is rotating + * the order of pre-configured values. */ + { + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES " + "values for testing"); + + for (i = 0; i < data->num_chal; i++) { + if (data->rand[i][0] == 0xaa) { + os_memcpy(data->kc[i], + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", + EAP_SIM_SRES_LEN); + } else if (data->rand[i][0] == 0xbb) { + os_memcpy(data->kc[i], + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", + EAP_SIM_SRES_LEN); + } else { + os_memcpy(data->kc[i], + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", + EAP_SIM_SRES_LEN); + } + } + } + + return 0; + +#else /* CONFIG_SIM_HARDCODED */ + + wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm " + "enabled"); + return -1; + +#endif /* CONFIG_SIM_HARDCODED */ +} + + +static int eap_sim_supported_ver(int version) +{ + return version == EAP_SIM_VERSION; +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + os_free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + os_free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + os_free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_sim_learn_ids(struct eap_sim_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + os_free(data->pseudonym); + data->pseudonym = os_malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next pseudonym"); + return -1; + } + os_memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + os_free(data->reauth_id); + data->reauth_id = os_malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next reauth_id"); + return -1; + } + os_memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, + int err) +{ + struct eap_sim_msg *msg; + + eap_sim_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id, + enum eap_sim_id_req id_req) +{ + const u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ) { + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + eap_sim_clear_identities(data, CLEAR_PSEUDONYM | + CLEAR_REAUTH_ID); + } + } + if (id_req != NO_ID_REQ) + eap_sim_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); + if (!data->reauth) { + wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", + data->selected_version); + eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, + data->selected_version, NULL, 0); + } + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_CHALLENGE); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres, + data->num_chal * EAP_SIM_SRES_LEN); +} + + +static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, + u8 id, int counter_too_small) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", + id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, + u8 id, u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id, + struct eap_sim_attrs *attr) +{ + int selected_version = -1, id_error; + size_t i; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); + if (attr->version_list == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " + "SIM/Start"); + return eap_sim_client_error(data, id, + EAP_SIM_UNSUPPORTED_VERSION); + } + + os_free(data->ver_list); + data->ver_list = os_malloc(attr->version_list_len); + if (data->ver_list == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " + "memory for version list"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + os_memcpy(data->ver_list, attr->version_list, attr->version_list_len); + data->ver_list_len = attr->version_list_len; + pos = data->ver_list; + for (i = 0; i < data->ver_list_len / 2; i++) { + int ver = pos[0] * 256 + pos[1]; + pos += 2; + if (eap_sim_supported_ver(ver)) { + selected_version = ver; + break; + } + } + if (selected_version < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " + "version"); + return eap_sim_client_error(data, id, + EAP_SIM_UNSUPPORTED_VERSION); + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", + selected_version); + data->selected_version = selected_version; + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " + "used within one authentication"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + return eap_sim_response_start(sm, data, id, attr->id_req); +} + + +static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + const u8 *identity; + size_t identity_len; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "did not include%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : ""); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", + (unsigned long) attr->num_chal); + if (attr->num_chal < data->min_num_chal) { + wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " + "challenges (%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(data, id, + EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); + } + if (attr->num_chal > 3) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " + "(%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Verify that RANDs are different */ + if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + (attr->num_chal > 2 && + (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + os_memcmp(attr->rand + GSM_RAND_LEN, + attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0))) { + wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); + return eap_sim_client_error(data, id, + EAP_SIM_RAND_NOT_FRESH); + } + + os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); + data->num_chal = attr->num_chal; + + if (eap_sim_gsm_auth(sm, data)) { + wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else + identity = eap_get_config_identity(sm, &identity_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " + "derivation", identity, identity_len); + eap_sim_derive_mk(identity, identity_len, data->nonce_mt, + data->selected_version, data->ver_list, + data->ver_list_len, data->num_chal, + (const u8 *) data->kc, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "used invalid AT_MAC"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + u8 *decrypted; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { + return eap_sim_client_error( + data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + eap_sim_learn_ids(data, &eattr); + os_free(decrypted); + } + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_sim_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + /* RFC 4186 specifies that counter is initialized to one after + * fullauth, but initializing it to zero makes it easier to implement + * reauth verification. */ + data->counter = 0; + return eap_sim_response_challenge(data, id); +} + + +static int eap_sim_process_notification_reauth(struct eap_sim_data *data, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " + "message does not match with counter in reauth " + "message"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + return 0; +} + + +static int eap_sim_process_notification_auth(struct eap_sim_data *data, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) + { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_sim_process_notification_reauth(data, attr)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_sim_process_notification( + struct eap_sm *sm, struct eap_sim_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-SIM: too many notification " + "rounds (only one allowed)"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " + "Notification message"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_sim_process_notification_auth(data, reqData, attr)) { + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); + if (attr->notification >= 0 && attr->notification < 32768) { + eap_sim_state(data, FAILURE); + } else if (attr->notification == EAP_SIM_SUCCESS && + data->state == RESULT_SUCCESS) + eap_sim_state(data, SUCCESS); + return eap_sim_response_notification(data, id, attr->notification); +} + + +static struct wpabuf * eap_sim_process_reauthentication( + struct eap_sm *sm, struct eap_sim_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) + { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "did not have valid AT_MAC"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + os_free(decrypted); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + os_free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + os_free(decrypted); + return eap_sim_response_reauth(data, id, 1); + } + data->counter = eattr.counter; + + os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk, + data->emsk); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_learn_ids(data, &eattr); + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_sim_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " + "fast reauths performed - force fullauth"); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + os_free(decrypted); + return eap_sim_response_reauth(data, id, 0); +} + + +static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_sim_data *data = priv; + const struct eap_hdr *req; + u8 subtype, id; + struct wpabuf *res; + const u8 *pos; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData); + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); + eap_sm_request_identity(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + req = wpabuf_head(reqData); + id = req->identifier; + len = be_to_host16(req->length); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0, + 0)) { + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_SIM_SUBTYPE_START: + res = eap_sim_process_start(sm, data, id, &attr); + break; + case EAP_SIM_SUBTYPE_CHALLENGE: + res = eap_sim_process_challenge(sm, data, id, reqData, &attr); + break; + case EAP_SIM_SUBTYPE_NOTIFICATION: + res = eap_sim_process_notification(sm, data, id, reqData, + &attr); + break; + case EAP_SIM_SUBTYPE_REAUTHENTICATION: + res = eap_sim_process_reauthentication(sm, data, id, reqData, + &attr); + break; + case EAP_SIM_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = data->use_result_ind ? + DECISION_UNCOND_SUCC : DECISION_COND_SUCC; + ret->methodState = data->use_result_ind ? + METHOD_DONE : METHOD_MAY_CONT; + } else if (data->state == RESULT_FAILURE) + ret->methodState = METHOD_CONT; + else if (data->state == RESULT_SUCCESS) + ret->methodState = METHOD_CONT; + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + eap_sim_clear_identities(data, CLEAR_EAP_ID); + data->use_result_ind = 0; +} + + +static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + os_free(data); + return NULL; + } + data->num_id_req = 0; + data->num_notification = 0; + eap_sim_state(data, CONTINUE); + return priv; +} + + +static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_sim_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_sim_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); + if (eap == NULL) + return -1; + + eap->init = eap_sim_init; + eap->deinit = eap_sim_deinit; + eap->process = eap_sim_process; + eap->isKeyAvailable = eap_sim_isKeyAvailable; + eap->getKey = eap_sim_getKey; + eap->has_reauth_data = eap_sim_has_reauth_data; + eap->deinit_for_reauth = eap_sim_deinit_for_reauth; + eap->init_for_reauth = eap_sim_init_for_reauth; + eap->get_identity = eap_sim_get_identity; + eap->get_emsk = eap_sim_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c new file mode 100644 index 0000000000000..31344a9136b29 --- /dev/null +++ b/src/eap_peer/eap_tls.c @@ -0,0 +1,289 @@ +/* + * EAP peer method: EAP-TLS (RFC 2716) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "tls.h" + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + u8 *key_data; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL || + ((sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL && + (sm->init_phase2 ? config->engine2 : config->engine) == 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (eap_peer_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); + sm->ignore = TRUE; + } else if (config->private_key && !config->private_key_passwd) + { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " + "key passphrase"); + eap_sm_request_passphrase(sm); + sm->ignore = TRUE; + } + return NULL; + } + + return data; +} + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + if (data == NULL) + return; + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + os_free(data); +} + + +static struct wpabuf * eap_tls_failure(struct eap_sm *sm, + struct eap_tls_data *data, + struct eap_method_ret *ret, int res, + struct wpabuf *resp, u8 id) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + + if (res == -1) { + struct eap_peer_config *config = eap_get_config(sm); + if (config) { + /* + * The TLS handshake failed. So better forget the old + * PIN. It may be wrong, we cannot be sure but trying + * the wrong one again might block it on the card--so + * better ask the user again. + */ + os_free(config->pin); + config->pin = NULL; + } + } + + if (resp) { + /* + * This is likely an alert message, so send it instead of just + * ACKing the error. + */ + return resp; + } + + return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); +} + + +static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, + struct eap_method_ret *ret) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + + os_free(data->key_data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK", + data->key_data + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + } else { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); + } +} + + +static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + struct wpabuf *resp; + u8 flags, id; + const u8 *pos; + struct eap_tls_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, + pos, left, &resp); + + if (res < 0) { + return eap_tls_failure(sm, data, ret, res, resp, id); + } + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + eap_tls_success(sm, data, ret); + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + } + + return resp; +} + + +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + return priv; +} + + +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_tls_data *data = priv; + return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->key_data != NULL; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c new file mode 100644 index 0000000000000..19afb90dcf2e7 --- /dev/null +++ b/src/eap_peer/eap_tls_common.c @@ -0,0 +1,1050 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "sha1.h" +#include "tls.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 || os_strncmp(*name, "blob://", 7) != 0) + return 0; + + blob = eap_get_config_blob(sm, *name + 7); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " + "found", __func__, *name + 7); + return -1; + } + + *name = NULL; + *data = blob->data; + *data_len = blob->len; + + return 0; +} + + +static void eap_tls_params_from_conf1(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert; + params->ca_path = (char *) config->ca_path; + params->client_cert = (char *) config->client_cert; + params->private_key = (char *) config->private_key; + params->private_key_passwd = (char *) config->private_key_passwd; + params->dh_file = (char *) config->dh_file; + params->subject_match = (char *) config->subject_match; + params->altsubject_match = (char *) config->altsubject_match; + params->engine = config->engine; + params->engine_id = config->engine_id; + params->pin = config->pin; + params->key_id = config->key_id; + params->cert_id = config->cert_id; + params->ca_cert_id = config->ca_cert_id; +} + + +static void eap_tls_params_from_conf2(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert2; + params->ca_path = (char *) config->ca_path2; + params->client_cert = (char *) config->client_cert2; + params->private_key = (char *) config->private_key2; + params->private_key_passwd = (char *) config->private_key2_passwd; + params->dh_file = (char *) config->dh_file2; + params->subject_match = (char *) config->subject_match2; + params->altsubject_match = (char *) config->altsubject_match2; + params->engine = config->engine2; + params->engine_id = config->engine2_id; + params->pin = config->pin2; + params->key_id = config->key2_id; + params->cert_id = config->cert2_id; + params->ca_cert_id = config->ca_cert2_id; +} + + +static int eap_tls_params_from_conf(struct eap_sm *sm, + struct eap_ssl_data *data, + struct tls_connection_params *params, + struct eap_peer_config *config, int phase2) +{ + os_memset(params, 0, sizeof(*params)); + if (phase2) { + wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); + eap_tls_params_from_conf2(params, config); + } else { + wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); + eap_tls_params_from_conf1(params, config); + } + params->tls_ia = data->tls_ia; + + /* + * Use blob data, if available. Otherwise, leave reference to external + * file as-is. + */ + if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob, + ¶ms->ca_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->client_cert, + ¶ms->client_cert_blob, + ¶ms->client_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->private_key, + ¶ms->private_key_blob, + ¶ms->private_key_blob_len) || + eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob, + ¶ms->dh_blob_len)) { + wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); + return -1; + } + + return 0; +} + + +static int eap_tls_init_connection(struct eap_sm *sm, + struct eap_ssl_data *data, + struct eap_peer_config *config, + struct tls_connection_params *params) +{ + int res; + + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + return -1; + } + + res = tls_connection_set_params(sm->ssl_ctx, data->conn, params); + if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + /* + * At this point with the pkcs11 engine the PIN might be wrong. + * We reset the PIN in the configuration to be sure to not use + * it again and the calling function must request a new one. + */ + os_free(config->pin); + config->pin = NULL; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + /* + * We do not know exactly but maybe the PIN was wrong, + * so ask for a new one. + */ + os_free(config->pin); + config->pin = NULL; + eap_sm_request_pin(sm); + sm->ignore = TRUE; + return -1; + } else if (res) { + wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " + "parameters"); + return -1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_init - Initialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @config: Pointer to the network configuration + * Returns: 0 on success, -1 on failure + * + * This function is used to initialize shared TLS functionality for EAP-TLS, + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config) +{ + struct tls_connection_params params; + + if (config == NULL) + return -1; + + data->eap = sm; + data->phase2 = sm->init_phase2; + if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < + 0) + return -1; + + if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) + return -1; + + data->tls_out_limit = config->fragment_size; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + + if (config->phase1 && + os_strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * + * This function deinitializes shared TLS functionality that was initialized + * with eap_peer_tls_ssl_init(). + */ +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); +} + + +/** + * eap_peer_tls_derive_key - Derive a key based on TLS session data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @len: Length of the key material to generate (usually 64 for MSK) + * Returns: Pointer to allocated key on success or %NULL on failure + * + * This function uses TLS-PRF to generate pseudo-random data based on the TLS + * session data (client/server random and master key). Each key type may use a + * different label to bind the key usage into the generated material. + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + /* First, try to use TLS library function for PRF, if available. */ + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == + 0) + return out; + + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + goto fail; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) + goto fail; + + os_free(rnd); + return out; + +fail: + os_free(out); + os_free(rnd); + return NULL; +} + + +/** + * eap_peer_tls_reassemble_fragment - Reassemble a received fragment + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @in_len: Length of in_data + * Returns: 0 on success, 1 if more data is needed for the full message, or + * -1 on error + */ +static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, + const u8 *in_data, size_t in_len) +{ + u8 *buf; + + if (data->tls_in_len + in_len == 0) { + /* No message data received?! */ + wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " + "tls_in_left=%lu tls_in_len=%lu in_len=%lu", + (unsigned long) data->tls_in_left, + (unsigned long) data->tls_in_len, + (unsigned long) in_len); + eap_peer_tls_reset_input(data); + return -1; + } + + if (data->tls_in_len + in_len > 65536) { + /* + * Limit length to avoid rogue servers from causing large + * memory allocations. + */ + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " + "64 kB)"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (in_len > data->tls_in_left) { + /* Sender is doing something odd - reject message */ + wpa_printf(MSG_INFO, "SSL: more data than TLS message length " + "indicated"); + eap_peer_tls_reset_input(data); + return -1; + } + + buf = os_realloc(data->tls_in, data->tls_in_len + in_len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " + "data"); + eap_peer_tls_reset_input(data); + return -1; + } + os_memcpy(buf + data->tls_in_len, in_data, in_len); + data->tls_in = buf; + data->tls_in_len += 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; + } + + return 0; +} + + +/** + * eap_peer_tls_data_reassemble - Reassemble TLS data + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @in_len: Length of in_data + * @out_len: Variable for returning length of the reassembled message + * @need_more_input: Variable for returning whether more input data is needed + * to reassemble this TLS packet + * Returns: Pointer to output data, %NULL on error or when more data is needed + * for the full message (in which case, *need_more_input is also set to 1). + * + * This function reassembles TLS fragments. Caller must not free the returned + * data buffer since an internal pointer to it is maintained. + */ +const u8 * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const u8 *in_data, size_t in_len, + size_t *out_len, int *need_more_input) +{ + *need_more_input = 0; + + if (data->tls_in_left > in_len || data->tls_in) { + /* Message has fragments */ + int res = eap_peer_tls_reassemble_fragment(data, in_data, + in_len); + if (res) { + if (res == 1) + *need_more_input = 1; + return NULL; + } + + /* Message is now fully reassembled. */ + } else { + /* No fragments in this message, so just make a copy of it. */ + data->tls_in_left = 0; + data->tls_in = os_malloc(in_len ? in_len : 1); + if (data->tls_in == NULL) + return NULL; + os_memcpy(data->tls_in, in_data, in_len); + data->tls_in_len = in_len; + } + + *out_len = data->tls_in_len; + return data->tls_in; +} + + +/** + * eap_tls_process_input - Process incoming TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to application data (if available) + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, -1 on failure + */ +static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, + const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + const u8 *msg; + size_t msg_len; + int need_more_input; + u8 *appl_data; + size_t appl_data_len; + + msg = eap_peer_tls_data_reassemble(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) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " + "tls_out data even though tls_out_len = 0"); + os_free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + appl_data = NULL; + data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, + msg, msg_len, + &data->tls_out_len, + &appl_data, &appl_data_len); + + eap_peer_tls_reset_input(data); + + if (appl_data && + tls_connection_established(sm->ssl_ctx, data->conn) && + !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application data", + appl_data, appl_data_len); + *out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len); + if (*out_data == NULL) { + os_free(appl_data); + return -1; + } + return 2; + } + + os_free(appl_data); + + return 0; +} + + +/** + * eap_tls_process_output - Process outgoing TLS message + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @ret: Return value to use on success + * @out_data: Buffer for returning the allocated output buffer + * Returns: ret (0 or 1) on success, -1 on failure + */ +static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, + int peap_version, u8 id, int ret, + struct wpabuf **out_data) +{ + size_t len; + u8 *flags; + int more_fragments, length_included; + + len = data->tls_out_len - data->tls_out_pos; + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) len, (unsigned long) data->tls_out_len); + + /* + * Limit outgoing message to the configured maximum size. Fragment + * message if needed. + */ + if (len > data->tls_out_limit) { + more_fragments = 1; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } else + more_fragments = 0; + + length_included = data->tls_out_pos == 0 && + (data->tls_out_len > data->tls_out_limit || + data->include_tls_length); + if (!length_included && + eap_type == EAP_TYPE_PEAP && peap_version == 0 && + !tls_connection_established(data->eap->ssl_ctx, data->conn)) { + /* + * Windows Server 2008 NPS really wants to have the TLS Message + * length included in phase 0 even for unfragmented frames or + * it will get very confused with Compound MAC calculation and + * Outer TLVs. + */ + length_included = 1; + } + + *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, + 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); + if (*out_data == NULL) + return -1; + + flags = wpabuf_put(*out_data, 1); + *flags = peap_version; + if (more_fragments) + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (length_included) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + wpabuf_put_be32(*out_data, data->tls_out_len); + } + + wpabuf_put_data(*out_data, &data->tls_out[data->tls_out_pos], len); + data->tls_out_pos += len; + + if (!more_fragments) + eap_peer_tls_reset_output(data); + + return ret; +} + + +/** + * eap_peer_tls_process_helper - Process TLS handshake message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to the response message + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, or -1 on failure + * + * This function can be used to process TLS handshake messages. It reassembles + * the received fragments and uses a TLS library to process the messages. The + * response data from the TLS library is fragmented to suitable output messages + * that the caller can send out. + * + * out_data is used to return the response message if the return value of this + * function is 0, 2, or -1. In case of failure, the message is likely a TLS + * alarm message. The caller is responsible for freeing the allocated buffer if + * *out_data is not %NULL. + * + * This function is called for each received TLS message during the TLS + * handshake after eap_peer_tls_process_init() call and possible processing of + * TLS Flags field. Once the handshake has been completed, i.e., when + * tls_connection_established() returns 1, EAP method specific decrypting of + * the tunneled data is used. + */ +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + int ret = 0; + + *out_data = NULL; + + if (data->tls_out_len > 0 && in_len > 0) { + wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " + "fragments are waiting to be sent out"); + return -1; + } + + if (data->tls_out_len == 0) { + /* + * No more data to send out - expect to receive more data from + * the AS. + */ + int res = eap_tls_process_input(sm, data, in_data, in_len, + out_data); + if (res) { + /* + * Input processing failed (res = -1) or more data is + * needed (res = 1). + */ + return res; + } + + /* + * The incoming message has been reassembled and processed. The + * response was allocated into data->tls_out buffer. + */ + } + + if (data->tls_out == NULL) { + /* + * No outgoing fragments remaining from the previous message + * and no new message generated. This indicates an error in TLS + * processing. + */ + eap_peer_tls_reset_output(data); + return -1; + } + + if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); + ret = -1; + /* TODO: clean pin if engine used? */ + } + + if (data->tls_out_len == 0) { + /* + * TLS negotiation should now be complete since all other cases + * needing more data should have been caught above based on + * the TLS Message Length field. + */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + os_free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + /* Send the pending message (in fragments, if needed). */ + return eap_tls_process_output(data, eap_type, peap_version, id, ret, + out_data); +} + + +/** + * eap_peer_tls_build_ack - Build a TLS ACK frame + * @id: EAP identifier for the response + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * Returns: Pointer to the allocated ACK frame or %NULL on failure + */ +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version) +{ + struct wpabuf *resp; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, + id); + if (resp == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", + (int) eap_type, id, peap_version); + wpabuf_put_u8(resp, peap_version); /* Flags */ + return resp; +} + + +/** + * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); + return tls_connection_shutdown(sm->ssl_ctx, data->conn); +} + + +/** + * eap_peer_tls_status - Get TLS status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + */ +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose) +{ + char name[128]; + int len = 0, ret; + + if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + ret = os_snprintf(buf + len, buflen - len, + "EAP TLS cipher=%s\n", name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} + + +/** + * eap_peer_tls_process_init - Initial validation/processing of EAP requests + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * @len: Buffer for returning length of the remaining payload + * @flags: Buffer for returning TLS flags + * Returns: Pointer to payload after TLS flags and length or %NULL on failure + * + * This function validates the EAP header and processes the optional TLS + * Message Length field. If this is the first fragment of a TLS message, the + * TLS reassembly code is initialized to receive the indicated number of bytes. + * + * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this + * function as the first step in processing received messages. They will need + * to process the flags (apart from Message Length Included) that are returned + * through the flags pointer and the message payload that will be returned (and + * the length is returned through the len pointer). Return values (ret) are set + * for continuation of EAP method processing. The caller is responsible for + * setting these to indicate completion (either success or failure) based on + * the authentication result. + */ +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags) +{ + const u8 *pos; + size_t left; + unsigned int tls_msg_len; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + if (left == 0) { + wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " + "octet included"); + if (!sm->workaround) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " + "indicates ACK frame"); + *flags = 0; + } else { + *flags = *pos++; + left--; + } + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), + *flags); + if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = WPA_GET_BE32(pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + if (data->tls_in_left == 0) { + data->tls_in_total = tls_msg_len; + data->tls_in_left = tls_msg_len; + os_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 = left; + return pos; +} + + +/** + * eap_peer_tls_reset_input - Reset input buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for input buffers and resets input + * state. + */ +void eap_peer_tls_reset_input(struct eap_ssl_data *data) +{ + data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; + os_free(data->tls_in); + data->tls_in = NULL; +} + + +/** + * eap_peer_tls_reset_output - Reset output buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for output buffers and resets + * output state. + */ +void eap_peer_tls_reset_output(struct eap_ssl_data *data) +{ + data->tls_out_len = 0; + data->tls_out_pos = 0; + os_free(data->tls_out); + data->tls_out = NULL; +} + + +/** + * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_decrypted: Buffer for returning a pointer to the decrypted message + * Returns: 0 on success, 1 if more input data is needed, or -1 on failure + */ +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted) +{ + int res; + const u8 *msg; + size_t msg_len, buf_len; + int need_more_input; + + msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data), + wpabuf_len(in_data), &msg_len, + &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + buf_len = wpabuf_len(in_data); + if (data->tls_in_total > buf_len) + buf_len = data->tls_in_total; + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf_len += 500; + buf_len *= 3; + *in_decrypted = wpabuf_alloc(buf_len ? buf_len : 1); + if (*in_decrypted == NULL) { + eap_peer_tls_reset_input(data); + wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for " + "decryption"); + return -1; + } + + res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len, + wpabuf_mhead(*in_decrypted), buf_len); + eap_peer_tls_reset_input(data); + if (res < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); + return -1; + } + wpabuf_put(*in_decrypted, res); + return 0; +} + + +/** + * eap_peer_tls_encrypt - Encrypt phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments + * @out_data: Buffer for returning a pointer to the encrypted response message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + int res; + size_t len; + + if (in_data) { + eap_peer_tls_reset_output(data); + len = wpabuf_len(in_data) + 300; + data->tls_out = os_malloc(len); + if (data->tls_out == NULL) + return -1; + + res = tls_connection_encrypt(sm->ssl_ctx, data->conn, + wpabuf_head(in_data), + wpabuf_len(in_data), + data->tls_out, len); + if (res < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " + "data (in_len=%lu)", + (unsigned long) wpabuf_len(in_data)); + eap_peer_tls_reset_output(data); + return -1; + } + + data->tls_out_len = res; + } + + return eap_tls_process_output(data, eap_type, peap_version, id, 0, + out_data); +} + + +/** + * eap_peer_select_phase2_methods - Select phase 2 EAP method + * @config: Pointer to the network configuration + * @prefix: 'phase2' configuration prefix, e.g., "auth=" + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * Returns: 0 on success, -1 on failure + * + * This function is used to parse EAP method list and select allowed methods + * for Phase2 authentication. + */ +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types) +{ + char *start, *pos, *buf; + struct eap_method_type *methods = NULL, *_methods; + u8 method; + size_t num_methods = 0, prefix_len; + + if (config == NULL || config->phase2 == NULL) + goto get_defaults; + + start = buf = os_strdup(config->phase2); + if (buf == NULL) + return -1; + + prefix_len = os_strlen(prefix); + + while (start && *start != '\0') { + int vendor; + pos = os_strstr(start, prefix); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + prefix_len; + continue; + } + + start = pos + prefix_len; + pos = os_strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start, &vendor); + if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " + "method '%s'", start); + } else { + num_methods++; + _methods = os_realloc(methods, + num_methods * sizeof(*methods)); + if (_methods == NULL) { + os_free(methods); + os_free(buf); + return -1; + } + methods = _methods; + methods[num_methods - 1].vendor = vendor; + methods[num_methods - 1].method = method; + } + + start = pos; + } + + os_free(buf); + +get_defaults: + if (methods == NULL) + methods = eap_get_phase2_types(config, &num_methods); + + if (methods == NULL) { + wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", + (u8 *) methods, + num_methods * sizeof(struct eap_method_type)); + + *types = methods; + *num_types = num_methods; + + return 0; +} + + +/** + * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * @hdr: EAP-Request header (and the following EAP type octet) + * @resp: Buffer for returning the EAP-Nak message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp) +{ + u8 *pos = (u8 *) (hdr + 1); + size_t i; + + /* TODO: add support for expanded Nak */ + wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", + (u8 *) types, num_types * sizeof(struct eap_method_type)); + *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, + EAP_CODE_RESPONSE, hdr->identifier); + if (*resp == NULL) + return -1; + + for (i = 0; i < num_types; i++) { + if (types[i].vendor == EAP_VENDOR_IETF && + types[i].method < 256) + wpabuf_put_u8(*resp, types[i].method); + } + + eap_update_len(*resp); + + return 0; +} diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h new file mode 100644 index 0000000000000..2c87427c28f57 --- /dev/null +++ b/src/eap_peer/eap_tls_common.h @@ -0,0 +1,139 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +/** + * struct eap_ssl_data - TLS data for EAP methods + */ +struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ + struct tls_connection *conn; + + /** + * tls_out - TLS message to be sent out in fragments + */ + u8 *tls_out; + + /** + * tls_out_len - Total length of the outgoing TLS message + */ + size_t tls_out_len; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ + size_t tls_out_limit; + + /** + * tls_in - Received TLS message buffer for re-assembly + */ + u8 *tls_in; + + /** + * tls_in_len - Number of bytes of the received TLS message in tls_in + */ + size_t tls_in_len; + + /** + * tls_in_left - Number of remaining bytes in the incoming TLS message + */ + size_t tls_in_left; + + /** + * tls_in_total - Total number of bytes in the incoming TLS message + */ + size_t tls_in_total; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * include_tls_length - Whether the TLS length field is included even + * if the TLS data is not fragmented + */ + int include_tls_length; + + /** + * tls_ia - Whether TLS/IA is enabled for this TLS connection + */ + int tls_ia; + + /** + * eap - Pointer to EAP state machine allocated with eap_peer_sm_init() + */ + struct eap_sm *eap; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_PEAP_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + + +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config); +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len); +const u8 * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const u8 *in_data, size_t in_len, + size_t *out_len, int *need_more_input); +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const u8 *in_data, size_t in_len, + struct wpabuf **out_data); +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version); +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose); +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags); +void eap_peer_tls_reset_input(struct eap_ssl_data *data); +void eap_peer_tls_reset_output(struct eap_ssl_data *data); +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted); +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data); +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types); +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c new file mode 100644 index 0000000000000..0a3a01c2a3b36 --- /dev/null +++ b/src/eap_peer/eap_tnc.c @@ -0,0 +1,428 @@ +/* + * EAP peer method: EAP-TNC (Trusted Network Connect) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "eap_i.h" +#include "tncc.h" + + +struct eap_tnc_data { + enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; + struct tncc_data *tncc; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; +}; + + +/* EAP-TNC Flags */ +#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TNC_FLAGS_START 0x20 +#define EAP_TNC_VERSION_MASK 0x07 + +#define EAP_TNC_VERSION 1 + + +static void * eap_tnc_init(struct eap_sm *sm) +{ + struct eap_tnc_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = WAIT_START; + data->fragment_size = 1300; + data->tncc = tncc_init(); + if (data->tncc == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_tnc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + tncc_deinit(data->tncc); + os_free(data); +} + + +static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 0, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); + + return msg; +} + + +static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response"); + ret->allowNotifications = TRUE; + + flags = EAP_TNC_VERSION; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + plen += 4; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + } else { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + data->state = WAIT_FRAG_ACK; + } + + return resp; +} + + +static int eap_tnc_process_cont(struct eap_tnc_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); + data->state = FAIL; + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for " + "%lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, + u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " + "fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_tnc_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + u8 *rpos, *rpos1; + size_t len, rlen; + size_t imc_len; + char *start_buf, *end_buf; + size_t start_len, end_len; + int tncs_done = 0; + u8 flags, id; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + end = pos + len; + + if (len == 0) + flags = 0; /* fragment ack */ + else + flags = *pos++; + + if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", + flags & EAP_TNC_VERSION_MASK); + ret->ignore = TRUE; + return NULL; + } + + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len != 0) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " + "WAIT_FRAG_ACK state"); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); + } + + if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { + return eap_tnc_process_fragment(data, ret, id, flags, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (data->state == WAIT_START) { + if (!(flags & EAP_TNC_FLAGS_START)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use " + "start flag in the first message"); + ret->ignore = TRUE; + return NULL; + } + + tncc_init_connection(data->tncc); + + data->state = PROC_MSG; + } else { + enum tncc_process_res res; + + if (flags & EAP_TNC_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start " + "flag again"); + ret->ignore = TRUE; + return NULL; + } + + res = tncc_process_if_tnccs(data->tncc, + wpabuf_head(data->in_buf), + wpabuf_len(data->in_buf)); + switch (res) { + case TNCCS_PROCESS_ERROR: + ret->ignore = TRUE; + return NULL; + case TNCCS_PROCESS_OK_NO_RECOMMENDATION: + case TNCCS_RECOMMENDATION_ERROR: + wpa_printf(MSG_DEBUG, "EAP-TNC: No " + "TNCCS-Recommendation received"); + break; + case TNCCS_RECOMMENDATION_ALLOW: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = allow"); + tncs_done = 1; + break; + case TNCCS_RECOMMENDATION_NONE: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = none"); + tncs_done = 1; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = isolate"); + tncs_done = 1; + break; + } + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = TRUE; + + if (data->out_buf) { + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); + } + + if (tncs_done) { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_TNC_VERSION); + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an " + "empty ACK message"); + return resp; + } + + imc_len = tncc_total_send_len(data->tncc); + + start_buf = tncc_if_tnccs_start(data->tncc); + if (start_buf == NULL) + return NULL; + start_len = os_strlen(start_buf); + end_buf = tncc_if_tnccs_end(); + if (end_buf == NULL) { + os_free(start_buf); + return NULL; + } + end_len = os_strlen(end_buf); + + rlen = start_len + imc_len + end_len; + resp = wpabuf_alloc(rlen); + if (resp == NULL) { + os_free(start_buf); + os_free(end_buf); + return NULL; + } + + wpabuf_put_data(resp, start_buf, start_len); + os_free(start_buf); + + rpos1 = wpabuf_put(resp, 0); + rpos = tncc_copy_send_buf(data->tncc, rpos1); + wpabuf_put(resp, rpos - rpos1); + + wpabuf_put_data(resp, end_buf, end_len); + os_free(end_buf); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response", + wpabuf_head(resp), wpabuf_len(resp)); + + data->out_buf = resp; + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); +} + + +int eap_peer_tnc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); + if (eap == NULL) + return -1; + + eap->init = eap_tnc_init; + eap->deinit = eap_tnc_deinit; + eap->process = eap_tnc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c new file mode 100644 index 0000000000000..e1a0fbd6c2e35 --- /dev/null +++ b/src/eap_peer/eap_ttls.c @@ -0,0 +1,1983 @@ +/* + * EAP peer method: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_peer/eap_tls_common.h" +#include "eap_peer/eap_config.h" +#include "ms_funcs.h" +#include "sha1.h" +#include "eap_common/chap.h" +#include "tls.h" +#include "mschapv2.h" +#include "eap_common/eap_ttls.h" + + +/* Maximum supported TTLS version + * 0 = RFC 5281 + * 1 = draft-funk-eap-ttls-v1-00.txt + */ +#ifndef EAP_TTLS_VERSION +#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ +#endif /* EAP_TTLS_VERSION */ + + +#define MSCHAPV2_KEY_LEN 16 +#define MSCHAPV2_NT_RESPONSE_LEN 24 + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + int ssl_initialized; + + int ttls_version, force_ttls_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_start; + + enum phase2_types { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + struct eap_method_type phase2_eap_type; + struct eap_method_type *phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + + struct wpabuf *pending_phase2_req; + +#ifdef EAP_TNC + int ready_for_tnc; + int tnc_started; +#endif /* EAP_TNC */ +}; + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + char *selected; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->ttls_version = EAP_TTLS_VERSION; + data->force_ttls_version = -1; + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + +#if EAP_TTLS_VERSION > 0 + if (config && config->phase1) { + const char *pos = os_strstr(config->phase1, "ttlsver="); + if (pos) { + data->force_ttls_version = atoi(pos + 8); + data->ttls_version = data->force_ttls_version; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version " + "%d", data->force_ttls_version); + } + } +#endif /* EAP_TTLS_VERSION */ + + if (config && config->phase2) { + if (os_strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (os_strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (os_strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (os_strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (eap_peer_select_phase2_methods(config, "autheap=", + &data->phase2_eap_types, + &data->num_phase2_eap_types) + < 0) { + eap_ttls_deinit(sm, data); + return NULL; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = EAP_TYPE_NONE; + } + +#if EAP_TTLS_VERSION > 0 + if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && + data->ttls_version > 0) { + if (data->force_ttls_version > 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " + "TLS library does not support TLS/IA.", + data->force_ttls_version); + eap_ttls_deinit(sm, data); + return NULL; + } + data->ttls_version = 0; + } +#endif /* EAP_TTLS_VERSION */ + + return data; +} + + +static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } +} + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + eap_ttls_phase2_eap_deinit(sm, data); + os_free(data->phase2_eap_types); + if (data->ssl_initialized) + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor *avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor *) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + const u8 *data, size_t len) +{ + u8 *pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + os_memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + + +static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, + int mandatory) +{ + struct wpabuf *msg; + u8 *avp, *pos; + + msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); + if (msg == NULL) { + wpabuf_free(*resp); + *resp = NULL; + return -1; + } + + avp = wpabuf_mhead(msg); + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); + os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); + pos += wpabuf_len(*resp); + AVP_PAD(avp, pos); + wpabuf_free(*resp); + wpabuf_put(msg, pos - avp); + *resp = msg; + return 0; +} + + +#if EAP_TTLS_VERSION > 0 +static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *key, size_t key_len) +{ + u8 *buf; + size_t buf_len; + int ret; + + if (key) { + buf_len = 2 + key_len; + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + WPA_PUT_BE16(buf, key_len); + os_memcpy(buf + 2, key, key_len); + } else { + buf = NULL; + buf_len = 0; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " + "secret permutation", buf, buf_len); + ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, + data->ssl.conn, + buf, buf_len); + os_free(buf); + + return ret; +} +#endif /* EAP_TTLS_VERSION */ + + +static int eap_ttls_v0_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + os_free(data->key_data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + if (!data->key_data) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + + return 0; +} + + +#if EAP_TTLS_VERSION > 0 +static int eap_ttls_v1_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct tls_keys keys; + u8 *rnd; + + os_free(data->key_data); + data->key_data = NULL; + + os_memset(&keys, 0, sizeof(keys)); + if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL || + keys.inner_secret == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " + "client random, or server random to derive keying " + "material"); + return -1; + } + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + data->key_data = os_malloc(EAP_TLS_KEY_LEN); + if (rnd == NULL || data->key_data == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); + os_free(rnd); + os_free(data->key_data); + data->key_data = NULL; + return -1; + } + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.inner_secret, keys.inner_secret_len, + "ttls v1 keying material", rnd, keys.client_random_len + + keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); + os_free(rnd); + os_free(data->key_data); + data->key_data = NULL; + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", + rnd, keys.client_random_len + keys.server_random_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", + keys.inner_secret, keys.inner_secret_len); + + os_free(rnd); + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + + return 0; +} +#endif /* EAP_TTLS_VERSION */ + + +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ +#if EAP_TTLS_VERSION > 0 + struct tls_keys keys; + u8 *challenge, *rnd; +#endif /* EAP_TTLS_VERSION */ + + if (data->ttls_version == 0) { + return eap_peer_tls_derive_key(sm, &data->ssl, + "ttls challenge", len); + } + +#if EAP_TTLS_VERSION > 0 + + os_memset(&keys, 0, sizeof(keys)); + if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL || + keys.inner_secret == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " + "client random, or server random to derive " + "implicit challenge"); + return NULL; + } + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + challenge = os_malloc(len); + if (rnd == NULL || challenge == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " + "challenge derivation"); + os_free(rnd); + os_free(challenge); + return NULL; + } + os_memcpy(rnd, keys.server_random, keys.server_random_len); + os_memcpy(rnd + keys.server_random_len, keys.client_random, + keys.client_random_len); + + if (tls_prf(keys.inner_secret, keys.inner_secret_len, + "inner application challenge", rnd, + keys.client_random_len + keys.server_random_len, + challenge, len)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " + "challenge"); + os_free(rnd); + os_free(challenge); + return NULL; + } + + os_free(rnd); + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", + challenge, len); + + return challenge; + +#else /* EAP_TTLS_VERSION */ + + return NULL; + +#endif /* EAP_TTLS_VERSION */ +} + + +static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret) +{ +#if EAP_TTLS_VERSION > 0 + if (data->ttls_version > 0) { + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; + + /* TTLSv1 requires TLS/IA FinalPhaseFinished */ + if (ret->decision == DECISION_UNCOND_SUCC) + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_CONT; + + if (ret->decision == DECISION_COND_SUCC && + m->isKeyAvailable && m->getKey && + m->isKeyAvailable(sm, priv)) { + u8 *key; + size_t key_len; + key = m->getKey(sm, priv, &key_len); + if (key) { + eap_ttls_ia_permute_inner_secret( + sm, data, key, key_len); + os_free(key); + } + } + } +#endif /* EAP_TTLS_VERSION */ +} + + +static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, + u8 method) +{ + size_t i; + for (i = 0; i < data->num_phase2_eap_types; i++) { + if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_eap_types[i].method != method) + continue; + + data->phase2_eap_type.vendor = + data->phase2_eap_types[i].vendor; + data->phase2_eap_type.method = + data->phase2_eap_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + break; + } +} + + +static int eap_ttls_phase2_eap_process(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + struct wpabuf **resp) +{ + struct wpabuf msg; + struct eap_method_ret iret; + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC || + iret.decision == DECISION_FAIL)) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + } + eap_ttlsv1_phase2_eap_finish(sm, data, ret); + + return 0; +} + + +static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + u8 method, struct wpabuf **resp) +{ +#ifdef EAP_TNC + if (data->tnc_started && data->phase2_method && + data->phase2_priv && method == EAP_TYPE_TNC && + data->phase2_eap_type.method == EAP_TYPE_TNC) + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, + resp); + + if (data->ready_for_tnc && !data->tnc_started && + method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "EAP method"); + data->tnc_started = 1; + } + + if (data->tnc_started) { + if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || + data->phase2_eap_type.method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " + "type %d for TNC", method); + return -1; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d (TNC)", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) + eap_ttls_phase2_eap_deinit(sm, data); + } +#endif /* EAP_TNC */ + + if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && + data->phase2_eap_type.method == EAP_TYPE_NONE) + eap_ttls_phase2_select_eap_method(data, method); + + if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) + { + if (eap_peer_tls_phase2_nak(data->phase2_eap_types, + data->num_phase2_eap_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + EAP_VENDOR_IETF, method); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " + "Phase 2 EAP method %d", method); + return -1; + } + + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); +} + + +static int eap_ttls_phase2_request_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-TTLS: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + break; + default: + if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, + *pos, resp) < 0) + return -1; + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + return 0; + } + + if (*resp == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", + *resp); + return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); +} + + +static void eap_ttlsv1_permute_inner(struct eap_sm *sm, + struct eap_ttls_data *data) +{ +#if EAP_TTLS_VERSION > 0 + u8 session_key[2 * MSCHAPV2_KEY_LEN]; + + if (data->ttls_version == 0) + return; + + get_asymetric_start_key(data->master_key, session_key, + MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(data->master_key, + session_key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 0); + eap_ttls_ia_permute_inner_secret(sm, data, session_key, + sizeof(session_key)); +#endif /* EAP_TTLS_VERSION */ +} + + +static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos, *challenge, *peer_challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "implicit challenge"); + return -1; + } + peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + /* MS-CHAP2-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAPV2_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 0; /* Flags */ + os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + os_memset(pos, 0, 8); /* Reserved, must be zero */ + pos += 8; + mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, challenge, + peer_challenge, pos, data->auth_response, + data->master_key); + data->auth_response_valid = 1; + + eap_ttlsv1_permute_inner(sm, data); + + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (sm->workaround && data->ttls_version == 0) { + /* At least FreeRADIUS seems to be terminating + * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success + * packet. */ + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " + "allow success without tunneled response"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +} + + +static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + + /* MS-CHAP-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAP_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 1; /* Flags: Use NT style passwords */ + os_memset(pos, 0, 24); /* LM-Response */ + pos += 24; + if (pwhash) { + challenge_response(challenge, password, pos); /* NT-Response */ + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", + password, 16); + } else { + nt_challenge_response(challenge, password, password_len, + pos); /* NT-Response */ + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", + password, password_len); + } + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (data->ttls_version > 0) { + /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, + * so do not allow connection to be terminated yet. */ + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + /* EAP-TTLS/MSCHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +} + + +static int eap_ttls_phase2_request_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos; + size_t pad; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + password_len + 100); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/PAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts + * the data, so no separate encryption is used in the AVP itself. + * However, the password is padded to obfuscate its length. */ + pad = (16 - (password_len & 15)) & 15; + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, + password_len + pad); + os_memcpy(pos, password, password_len); + pos += password_len; + os_memset(pos, 0, pad); + pos += pad; + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (data->ttls_version > 0) { + /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, + * so do not allow connection to be terminated yet. */ + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + /* EAP-TTLS/PAP does not provide tunneled success notification, + * so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +} + + +static int eap_ttls_phase2_request_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/CHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + + /* CHAP-Password */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, + 1 + EAP_TTLS_CHAP_PASSWORD_LEN); + data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + + /* MD5(Ident + Password + Challenge) */ + chap_md5(data->ident, password, password_len, challenge, + EAP_TTLS_CHAP_CHALLENGE_LEN, pos); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", + identity, identity_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", + password, password_len); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", + pos, EAP_TTLS_CHAP_PASSWORD_LEN); + pos += EAP_TTLS_CHAP_PASSWORD_LEN; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (data->ttls_version > 0) { + /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, + * so do not allow connection to be terminated yet. */ + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + /* EAP-TTLS/CHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +} + + +static int eap_ttls_phase2_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + int res = 0; + size_t len; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); + phase2_type = EAP_TTLS_PHASE2_EAP; + } +#endif /* EAP_TNC */ + + if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || + phase2_type == EAP_TTLS_PHASE2_MSCHAP || + phase2_type == EAP_TTLS_PHASE2_PAP || + phase2_type == EAP_TTLS_PHASE2_CHAP) { + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Identity not configured"); + eap_sm_request_identity(sm); + if (eap_get_config_password(sm, &len) == NULL) + eap_sm_request_password(sm); + return 0; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Password not configured"); + eap_sm_request_password(sm); + return 0; + } + } + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_MSCHAP: + res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_PAP: + res = eap_ttls_phase2_request_pap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_CHAP: + res = eap_ttls_phase2_request_chap(sm, data, ret, resp); + break; + default: + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); + res = -1; + break; + } + + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return res; +} + + +#if EAP_TTLS_VERSION > 0 +static struct wpabuf * eap_ttls_build_phase_finished( + struct eap_sm *sm, struct eap_ttls_data *data, int id, int final) +{ + int len; + struct wpabuf *req; + u8 *pos; + const int max_len = 300; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1 + max_len, + EAP_CODE_RESPONSE, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, data->ttls_version); + + pos = wpabuf_put(req, 0); + len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, + data->ssl.conn, + final, pos, max_len); + if (len < 0) { + wpabuf_free(req); + return NULL; + } + wpabuf_put(req, len); + eap_update_len(req); + + return req; +} +#endif /* EAP_TTLS_VERSION */ + + +struct ttls_parse_avp { + u8 *mschapv2; + u8 *eapdata; + size_t eap_len; + int mschapv2_error; +}; + + +static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, + struct ttls_parse_avp *parse) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (parse->eapdata == NULL) { + parse->eapdata = os_malloc(dlen); + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(parse->eapdata, dpos, dlen); + parse->eap_len = dlen; + } else { + u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); + if (neweap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eapdata = neweap; + parse->eap_len += dlen; + } + + return 0; +} + + +static int eap_ttls_parse_avp(u8 *pos, size_t left, + struct ttls_parse_avp *parse) +{ + struct ttls_avp *avp; + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t dlen; + + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + + if (avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%lu) - dropped", + (int) avp_length, (unsigned long) left); + return -1; + } + + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", + avp_length); + return -1; + } + + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " + "underflow"); + return -1; + } + vendor_id = WPA_GET_BE32(dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) + return -1; + } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { + /* This is an optional message that can be displayed to + * the user. */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", + dpos, dlen); + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", + dpos, dlen); + if (dlen != 43) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " + "MS-CHAP2-Success length " + "(len=%lu, expected 43)", + (unsigned long) dlen); + return -1; + } + parse->mschapv2 = dpos; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", + dpos, dlen); + parse->mschapv2_error = 1; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " + "code %d vendor_id %d - dropped", + (int) avp_code, (int) vendor_id); + return -1; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " + "code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + return avp_length; +} + + +static int eap_ttls_parse_avps(struct wpabuf *in_decrypted, + struct ttls_parse_avp *parse) +{ + u8 *pos; + size_t left, pad; + int avp_length; + + pos = wpabuf_mhead(in_decrypted); + left = wpabuf_len(in_decrypted); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); + if (left < sizeof(struct ttls_avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" + " len=%lu expected %lu or more - dropped", + (unsigned long) left, + (unsigned long) sizeof(struct ttls_avp)); + return -1; + } + + /* Parse AVPs */ + os_memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + avp_length = eap_ttls_parse_avp(pos, left, parse); + if (avp_length < 0) + return -1; + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + if (left < avp_length + pad) + left = 0; + else + left -= avp_length + pad; + } + + return 0; +} + + +static u8 * eap_ttls_fake_identity_request(void) +{ + struct eap_hdr *hdr; + u8 *buf; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " + "Phase 2 - use fake EAP-Request Identity"); + buf = os_malloc(sizeof(*hdr) + 1); + if (buf == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " + "memory for fake EAP-Identity Request"); + return NULL; + } + + hdr = (struct eap_hdr *) buf; + hdr->code = EAP_CODE_REQUEST; + hdr->identifier = 0; + hdr->length = host_to_be16(sizeof(*hdr) + 1); + buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; + + return buf; +} + + +static int eap_ttls_encrypt_response(struct eap_sm *sm, + struct eap_ttls_data *data, + struct wpabuf *resp, u8 identifier, + struct wpabuf **out_data) +{ + if (resp == NULL) + return 0; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", + resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " + "frame"); + return -1; + } + wpabuf_free(resp); + + return 0; +} + + +static int eap_ttls_process_phase2_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + struct eap_hdr *hdr; + size_t len; + + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " + "packet - dropped"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", + parse->eapdata, parse->eap_len); + hdr = (struct eap_hdr *) parse->eapdata; + + if (parse->eap_len < sizeof(*hdr)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " + "frame (len=%lu, expected %lu or more) - dropped", + (unsigned long) parse->eap_len, + (unsigned long) sizeof(*hdr)); + return -1; + } + len = be_to_host16(hdr->length); + if (len > parse->eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " + "EAP frame (EAP hdr len=%lu, EAP data len in " + "AVP=%lu)", + (unsigned long) len, + (unsigned long) parse->eap_len); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " + "identifier=%d length=%lu", + hdr->code, hdr->identifier, (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + return -1; + } + + return 0; +} + + +static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse) +{ + if (parse->mschapv2_error) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " + "MS-CHAP-Error - failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Reply with empty data to ACK error */ + return 1; + } + + if (parse->mschapv2 == NULL) { +#ifdef EAP_TNC + if (data->phase2_success && parse->eapdata) { + /* + * Allow EAP-TNC to be started after successfully + * completed MSCHAPV2. + */ + return 1; + } +#endif /* EAP_TNC */ + wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " + "received for Phase2 MSCHAPV2"); + return -1; + } + if (parse->mschapv2[0] != data->ident) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " + "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", + parse->mschapv2[0], data->ident); + return -1; + } + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, + parse->mschapv2 + 1, 42)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " + "response in Phase 2 MSCHAPV2 success request"); + return -1; + } + + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " + "authentication succeeded"); + if (data->ttls_version > 0) { + /* + * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report + * success, so do not allow connection to be terminated + * yet. + */ + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + } + + /* + * Reply with empty data; authentication server will reply + * with EAP-Success after this. + */ + return 1; +} + + +#ifdef EAP_TNC +static int eap_ttls_process_tnc_start(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ + if (parse->eapdata == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "unexpected tunneled data (no EAP)"); + return -1; + } + + if (!data->ready_for_tnc) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "EAP after non-EAP, but not ready for TNC"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "non-EAP method"); + data->tnc_started = 1; + + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) + return -1; + + return 0; +} +#endif /* EAP_TNC */ + + +static int eap_ttls_process_decrypted(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct ttls_parse_avp *parse, + struct wpabuf *in_decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL; + struct eap_peer_config *config = eap_get_config(sm); + int res; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) + phase2_type = EAP_TTLS_PHASE2_EAP; +#endif /* EAP_TNC */ + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < + 0) + return -1; + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); +#ifdef EAP_TNC + if (res == 1 && parse->eapdata && data->phase2_success) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + if (eap_ttls_process_tnc_start(sm, data, ret, parse, + &resp) == 0) + break; + } +#endif /* EAP_TNC */ + return res; + case EAP_TTLS_PHASE2_MSCHAP: + case EAP_TTLS_PHASE2_PAP: + case EAP_TTLS_PHASE2_CHAP: +#ifdef EAP_TNC + if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < + 0) + return -1; + break; +#else /* EAP_TNC */ + /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled + * requests to the supplicant */ + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " + "tunneled data"); + return -1; +#endif /* EAP_TNC */ + } + + if (resp) { + if (eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data) < 0) + return -1; + } else if (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_dup(in_decrypted); + } + + return 0; +} + + +#if EAP_TTLS_VERSION > 0 +static void eap_ttls_final_phase_finished(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct wpabuf **out_data) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received"); + wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1); + eap_ttls_v1_derive_key(sm, data); +} +#endif /* EAP_TTLS_VERSION */ + + +static int eap_ttls_implicit_identity_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct wpabuf **out_data) +{ + int retval = 0; + struct eap_hdr *hdr; + struct wpabuf *resp; + + hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); + if (hdr == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + resp = NULL; + if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + retval = -1; + } else { + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data); + } + + os_free(hdr); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + struct wpabuf **out_data) +{ + data->phase2_start = 0; + + /* + * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only + * if TLS part was indeed resuming a previous session. Most + * Authentication Servers terminate EAP-TTLS before reaching this + * point, but some do not. Make wpa_supplicant stop phase 2 here, if + * needed. + */ + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " + "skip phase 2"); + *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, + data->ttls_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + return 0; + } + + return eap_ttls_implicit_identity_request(sm, data, ret, identifier, + out_data); +} + + +static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted = NULL; + int retval = 0; + struct ttls_parse_avp parse; + + os_memset(&parse, 0, sizeof(parse)); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", + in_data ? (unsigned long) wpabuf_len(in_data) : 0); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + return eap_ttls_implicit_identity_request( + sm, data, ret, identifier, out_data); + } + goto continue_req; + } + + if ((in_data == NULL || wpabuf_len(in_data) == 0) && + data->phase2_start) { + return eap_ttls_phase2_start(sm, data, ret, identifier, + out_data); + } + + if (in_data == NULL || wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, + identifier, NULL, out_data); + } + + retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (retval) + goto done; + +#if EAP_TTLS_VERSION > 0 + if (data->ttls_version > 0 && + (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) && + tls_connection_ia_final_phase_finished(sm->ssl_ctx, + data->ssl.conn)) { + eap_ttls_final_phase_finished(sm, data, ret, identifier, + out_data); + goto done; + } +#endif /* EAP_TTLS_VERSION */ + +continue_req: + data->phase2_start = 0; + + if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { + retval = -1; + goto done; + } + + retval = eap_ttls_process_decrypted(sm, data, ret, identifier, + &parse, in_decrypted, out_data); + +done: + wpabuf_free(in_decrypted); + os_free(parse.eapdata); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_process_start(struct eap_sm *sm, + struct eap_ttls_data *data, u8 flags, + struct eap_method_ret *ret) +{ + struct eap_peer_config *config = eap_get_config(sm); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)", + flags & EAP_PEAP_VERSION_MASK, data->ttls_version); +#if EAP_TTLS_VERSION > 0 + if ((flags & EAP_PEAP_VERSION_MASK) < data->ttls_version) + data->ttls_version = flags & EAP_PEAP_VERSION_MASK; + if (data->force_ttls_version >= 0 && + data->force_ttls_version != data->ttls_version) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " + "forced TTLS version %d", + data->force_ttls_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d", + data->ttls_version); + + if (data->ttls_version > 0) + data->ssl.tls_ia = 1; +#endif /* EAP_TTLS_VERSION */ + if (!data->ssl_initialized && + eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + return -1; + } + data->ssl_initialized = 1; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); + + return 0; +} + + +static int eap_ttls_process_handshake(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + int res; + + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + in_data, in_len, out_data); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " + "Phase 2"); + if (data->resuming) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " + "skip Phase 2"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_MAY_CONT; + } + data->phase2_start = 1; + if (data->ttls_version == 0) + eap_ttls_v0_derive_key(sm, data); + + if (*out_data == NULL || wpabuf_len(*out_data) == 0) { + if (eap_ttls_decrypt(sm, data, ret, identifier, + NULL, out_data)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to process early " + "start for Phase 2"); + } + res = 0; + } + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = *out_data; + *out_data = NULL; + wpabuf_set(&msg, in_data, in_len); + res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + out_data); + } + + return res; +} + + +static void eap_ttls_check_auth_status(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret) +{ + if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + if (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully"); + data->phase2_success = 1; +#ifdef EAP_TNC + if (!data->ready_for_tnc && !data->tnc_started) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + } +#endif /* EAP_TNC */ + } + } else if (data->ttls_version == 0 && + ret->methodState == METHOD_MAY_CONT && + (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully (MAY_CONT)"); + data->phase2_success = 1; + } +} + + +static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_ttls_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + if (eap_ttls_process_start(sm, data, flags, ret) < 0) + return NULL; + + /* RFC 5281, Ch. 9.2: + * "This packet MAY contain additional information in the form + * of AVPs, which may provide useful hints to the client" + * For now, ignore any potential extra data. + */ + left = 0; + } else if (!data->ssl_initialized) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not " + "include Start flag"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); + } else { + res = eap_ttls_process_handshake(sm, data, ret, id, + pos, left, &resp); + } + + eap_ttls_check_auth_status(sm, data, ret); + + /* FIX: what about res == -1? Could just move all error processing into + * the other functions and get rid of this res==1 case here. */ + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + return resp; +} + + +static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +#ifdef EAP_TNC + data->ready_for_tnc = 0; + data->tnc_started = 0; +#endif /* EAP_TNC */ +} + + +static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_start = 0; + data->phase2_success = 0; + data->resuming = 1; + data->reauth = 1; + return priv; +} + + +static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_ttls_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + ret = os_snprintf(buf + len, buflen - len, + "EAP-TTLSv%d Phase2 method=", + data->ttls_version); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + switch (data->phase2_type) { + case EAP_TTLS_PHASE2_EAP: + ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", + data->phase2_method ? + data->phase2_method->name : "?"); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); + break; + case EAP_TTLS_PHASE2_MSCHAP: + ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); + break; + case EAP_TTLS_PHASE2_PAP: + ret = os_snprintf(buf + len, buflen - len, "PAP\n"); + break; + case EAP_TTLS_PHASE2_CHAP: + ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); + break; + default: + ret = 0; + break; + } + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +int eap_peer_ttls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->deinit = eap_ttls_deinit; + eap->process = eap_ttls_process; + eap->isKeyAvailable = eap_ttls_isKeyAvailable; + eap->getKey = eap_ttls_getKey; + eap->get_status = eap_ttls_get_status; + eap->has_reauth_data = eap_ttls_has_reauth_data; + eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; + eap->init_for_reauth = eap_ttls_init_for_reauth; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c new file mode 100644 index 0000000000000..3e114c142a428 --- /dev/null +++ b/src/eap_peer/eap_vendor_test.c @@ -0,0 +1,195 @@ +/* + * EAP peer method: Test method for vendor specific (expanded) EAP type + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a vendor specific test method using EAP expanded types. + * This is only for test use and must not be used for authentication since no + * security is provided. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#ifdef TEST_PENDING_REQUEST +#include "eloop.h" +#endif /* TEST_PENDING_REQUEST */ + + +#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_TYPE 0xfcfbfaf9 + + +/* #define TEST_PENDING_REQUEST */ + +struct eap_vendor_test_data { + enum { INIT, CONFIRM, SUCCESS } state; + int first_try; +}; + + +static void * eap_vendor_test_init(struct eap_sm *sm) +{ + struct eap_vendor_test_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = INIT; + data->first_try = 1; + return data; +} + + +static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + os_free(data); +} + + +#ifdef TEST_PENDING_REQUEST +static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) +{ + struct eap_sm *sm = eloop_ctx; + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending " + "request"); + eap_notify_pending(sm); +} +#endif /* TEST_PENDING_REQUEST */ + + +static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_vendor_test_data *data = priv; + struct wpabuf *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + if (data->state == INIT && *pos != 1) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "%d in INIT state", *pos); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == CONFIRM && *pos != 3) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "%d in CONFIRM state", *pos); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "in SUCCESS state"); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == CONFIRM) { +#ifdef TEST_PENDING_REQUEST + if (data->first_try) { + data->first_try = 0; + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing " + "pending request"); + ret->ignore = TRUE; + eloop_register_timeout(1, 0, eap_vendor_ready, sm, + NULL); + return NULL; + } +#endif /* TEST_PENDING_REQUEST */ + } + + ret->ignore = FALSE; + + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response"); + ret->allowNotifications = TRUE; + + resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + if (data->state == INIT) { + wpabuf_put_u8(resp, 2); + data->state = CONFIRM; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + } else { + wpabuf_put_u8(resp, 4); + data->state = SUCCESS; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + + return resp; +} + + +static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_vendor_test_data *data = priv; + u8 *key; + const int key_len = 64; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + os_memset(key, 0x11, key_len / 2); + os_memset(key + key_len / 2, 0x22, key_len / 2); + *len = key_len; + + return key; +} + + +int eap_peer_vendor_test_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_ID, EAP_VENDOR_TYPE, + "VENDOR-TEST"); + if (eap == NULL) + return -1; + + eap->init = eap_vendor_test_init; + eap->deinit = eap_vendor_test_deinit; + eap->process = eap_vendor_test_process; + eap->isKeyAvailable = eap_vendor_test_isKeyAvailable; + eap->getKey = eap_vendor_test_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c new file mode 100644 index 0000000000000..17e42f4773682 --- /dev/null +++ b/src/eap_peer/eap_wsc.c @@ -0,0 +1,453 @@ +/* + * EAP-WSC peer for Wi-Fi Protected Setup + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" +#include "eap_i.h" +#include "eap_common/eap_wsc_common.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" + + +struct eap_wsc_data { + enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + int registrar; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + enum wsc_op_code in_op_code, out_op_code; + size_t out_used; + size_t fragment_size; + struct wps_data *wps; + struct wps_context *wps_ctx; +}; + + +static const char * eap_wsc_state_txt(int state) +{ + switch (state) { + case WAIT_START: + return "WAIT_START"; + case MESG: + return "MESG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_wsc_state(struct eap_wsc_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", + eap_wsc_state_txt(data->state), + eap_wsc_state_txt(state)); + data->state = state; +} + + +static void * eap_wsc_init(struct eap_sm *sm) +{ + struct eap_wsc_data *data; + const u8 *identity; + size_t identity_len; + int registrar; + struct wps_config cfg; + const char *pos; + const char *phase1; + struct wps_context *wps; + + wps = sm->wps; + if (wps == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); + return NULL; + } + + identity = eap_get_config_identity(sm, &identity_len); + + if (identity && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) + registrar = 1; /* Supplicant is Registrar */ + else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) + registrar = 0; /* Supplicant is Enrollee */ + else { + wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", + identity, identity_len); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = registrar ? MESG : WAIT_START; + data->registrar = registrar; + data->wps_ctx = wps; + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = wps; + cfg.registrar = registrar; + + phase1 = eap_get_config_phase1(sm); + if (phase1 == NULL) { + wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " + "set"); + os_free(data); + return NULL; + } + + pos = os_strstr(phase1, "pin="); + if (pos) { + pos += 4; + cfg.pin = (const u8 *) pos; + while (*pos != '\0' && *pos != ' ') + pos++; + cfg.pin_len = pos - (const char *) cfg.pin; + } else { + pos = os_strstr(phase1, "pbc=1"); + if (pos) + cfg.pbc = 1; + } + + if (cfg.pin == NULL && !cfg.pbc) { + wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " + "configuration data"); + os_free(data); + return NULL; + } + + data->wps = wps_init(&cfg); + if (data->wps == NULL) { + os_free(data); + return NULL; + } + data->fragment_size = WSC_FRAGMENT_SIZE; + + if (registrar && cfg.pin) { + wps_registrar_add_pin(data->wps_ctx->registrar, NULL, + cfg.pin, cfg.pin_len); + } + + return data; +} + + +static void eap_wsc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + wps_deinit(data->wps); + os_free(data->wps_ctx->network_key); + data->wps_ctx->network_key = NULL; + os_free(data); +} + + +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); + ret->allowNotifications = TRUE; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (2 + send_len > data->fragment_size) { + send_len = data->fragment_size - 2; + flags |= WSC_FLAGS_MF; + if (data->out_used == 0) { + flags |= WSC_FLAGS_LF; + send_len -= 2; + } + } + plen = 2 + send_len; + if (flags & WSC_FLAGS_LF) + plen += 2; + resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & WSC_FLAGS_LF) + wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + if ((data->state == FAIL && data->out_op_code == WSC_ACK) || + data->out_op_code == WSC_NACK || + data->out_op_code == WSC_Done) { + eap_wsc_state(data, FAIL); + ret->methodState = METHOD_DONE; + } else + eap_wsc_state(data, MESG); + } else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_wsc_state(data, WAIT_FRAG_ACK); + } + + return resp; +} + + +static int eap_wsc_process_cont(struct eap_wsc_data *data, + const u8 *buf, size_t len, u8 op_code) +{ + /* Process continuation of a pending message */ + if (op_code != data->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " + "fragment (expected %d)", + op_code, data->in_op_code); + return -1; + } + + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + eap_wsc_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " + "for %lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, u8 op_code, + u16 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " + "fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + data->in_op_code = op_code; + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_wsc_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 op_code, flags, id; + u16 message_length = 0; + enum wps_process_res res; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, + &len); + if (pos == NULL || len < 2) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + start = pos; + end = start + len; + + op_code = *pos++; + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE16(pos); + pos += 2; + + if (message_length < end - pos) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " + "Length"); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " + "Flags 0x%x Message Length %d", + op_code, flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (op_code != WSC_FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_FRAG_ACK state", op_code); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); + eap_wsc_state(data, MESG); + return eap_wsc_build_msg(data, ret, id); + } + + if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && + op_code != WSC_Done && op_code != WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == WAIT_START) { + if (op_code != WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_START state", op_code); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); + eap_wsc_state(data, MESG); + /* Start message has empty payload, skip processing */ + goto send_msg; + } else if (op_code == WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf && + eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & WSC_FLAGS_MF) { + return eap_wsc_process_fragment(data, ret, id, flags, op_code, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + res = wps_process_msg(data->wps, op_code, data->in_buf); + switch (res) { + case WPS_DONE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " + "successfully - wait for EAP failure"); + eap_wsc_state(data, FAIL); + break; + case WPS_CONTINUE: + eap_wsc_state(data, MESG); + break; + case WPS_FAILURE: + case WPS_PENDING: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); + eap_wsc_state(data, FAIL); + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + +send_msg: + if (data->out_buf == NULL) { + data->out_buf = wps_get_msg(data->wps, &data->out_op_code); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " + "message from WPS"); + return NULL; + } + data->out_used = 0; + } + + eap_wsc_state(data, MESG); + return eap_wsc_build_msg(data, ret, id); +} + + +int eap_peer_wsc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + "WSC"); + if (eap == NULL) + return -1; + + eap->init = eap_wsc_init; + eap->deinit = eap_wsc_deinit; + eap->process = eap_wsc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c new file mode 100644 index 0000000000000..9172e1f3481d4 --- /dev/null +++ b/src/eap_peer/ikev2.c @@ -0,0 +1,1303 @@ +/* + * IKEv2 responder (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "dh_groups.h" +#include "ikev2.h" + + +void ikev2_responder_deinit(struct ikev2_responder_data *data) +{ + ikev2_free_keys(&data->keys); + wpabuf_free(data->i_dh_public); + wpabuf_free(data->r_dh_private); + os_free(data->IDi); + os_free(data->IDr); + os_free(data->shared_secret); + wpabuf_free(data->i_sign_msg); + wpabuf_free(data->r_sign_msg); + os_free(data->key_pad); +} + + +static int ikev2_derive_keys(struct ikev2_responder_data *data) +{ + u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; + size_t buf_len, pad_len; + struct wpabuf *shared; + const struct ikev2_integ_alg *integ; + const struct ikev2_prf_alg *prf; + const struct ikev2_encr_alg *encr; + int ret; + const u8 *addr[2]; + size_t len[2]; + + /* RFC 4306, Sect. 2.14 */ + + integ = ikev2_get_integ(data->proposal.integ); + prf = ikev2_get_prf(data->proposal.prf); + encr = ikev2_get_encr(data->proposal.encr); + if (integ == NULL || prf == NULL || encr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); + return -1; + } + + shared = dh_derive_shared(data->i_dh_public, data->r_dh_private, + data->dh); + if (shared == NULL) + return -1; + + /* Construct Ni | Nr | SPIi | SPIr */ + + buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; + buf = os_malloc(buf_len); + if (buf == NULL) { + wpabuf_free(shared); + return -1; + } + + pos = buf; + os_memcpy(pos, data->i_nonce, data->i_nonce_len); + pos += data->i_nonce_len; + os_memcpy(pos, data->r_nonce, data->r_nonce_len); + pos += data->r_nonce_len; + os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); + pos += IKEV2_SPI_LEN; + os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); +#ifdef CCNS_PL +#if __BYTE_ORDER == __LITTLE_ENDIAN + { + int i; + u8 *tmp = pos - IKEV2_SPI_LEN; + /* Incorrect byte re-ordering on little endian hosts.. */ + for (i = 0; i < IKEV2_SPI_LEN; i++) + *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i]; + for (i = 0; i < IKEV2_SPI_LEN; i++) + *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i]; + } +#endif +#endif /* CCNS_PL */ + + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + /* Use zero-padding per RFC 4306, Sect. 2.14 */ + pad_len = data->dh->prime_len - wpabuf_len(shared); +#ifdef CCNS_PL + /* Shared secret is not zero-padded correctly */ + pad_len = 0; +#endif /* CCNS_PL */ + pad = os_zalloc(pad_len ? pad_len : 1); + if (pad == NULL) { + wpabuf_free(shared); + os_free(buf); + return -1; + } + + addr[0] = pad; + len[0] = pad_len; + addr[1] = wpabuf_head(shared); + len[1] = wpabuf_len(shared); + if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, + 2, addr, len, skeyseed) < 0) { + wpabuf_free(shared); + os_free(buf); + os_free(pad); + return -1; + } + os_free(pad); + wpabuf_free(shared); + + /* DH parameters are not needed anymore, so free them */ + wpabuf_free(data->i_dh_public); + data->i_dh_public = NULL; + wpabuf_free(data->r_dh_private); + data->r_dh_private = NULL; + + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", + skeyseed, prf->hash_len); + + ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, + &data->keys); + os_free(buf); + return ret; +} + + +static int ikev2_parse_transform(struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + int transform_len; + const struct ikev2_transform *t; + u16 transform_id; + const u8 *tend; + + if (end - pos < (int) sizeof(*t)) { + wpa_printf(MSG_INFO, "IKEV2: Too short transform"); + return -1; + } + + t = (const struct ikev2_transform *) pos; + transform_len = WPA_GET_BE16(t->transform_length); + if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", + transform_len); + return -1; + } + tend = pos + transform_len; + + transform_id = WPA_GET_BE16(t->transform_id); + + wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " + "Transform Type: %d Transform ID: %d", + t->type, transform_len, t->transform_type, transform_id); + + if (t->type != 0 && t->type != 3) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); + return -1; + } + + pos = (const u8 *) (t + 1); + if (pos < tend) { + wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", + pos, tend - pos); + } + + switch (t->transform_type) { + case IKEV2_TRANSFORM_ENCR: + if (ikev2_get_encr(transform_id)) { + if (transform_id == ENCR_AES_CBC) { + if (tend - pos != 4) { + wpa_printf(MSG_DEBUG, "IKEV2: No " + "Transform Attr for AES"); + break; + } +#ifdef CCNS_PL + if (WPA_GET_BE16(pos) != 0x001d /* ?? */) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } +#else /* CCNS_PL */ + if (WPA_GET_BE16(pos) != 0x800e) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } +#endif /* CCNS_PL */ + if (WPA_GET_BE16(pos + 2) != 128) { + wpa_printf(MSG_DEBUG, "IKEV2: " + "Unsupported AES key size " + "%d bits", + WPA_GET_BE16(pos + 2)); + break; + } + } + prop->encr = transform_id; + } + break; + case IKEV2_TRANSFORM_PRF: + if (ikev2_get_prf(transform_id)) + prop->prf = transform_id; + break; + case IKEV2_TRANSFORM_INTEG: + if (ikev2_get_integ(transform_id)) + prop->integ = transform_id; + break; + case IKEV2_TRANSFORM_DH: + if (dh_groups_get(transform_id)) + prop->dh = transform_id; + break; + } + + return transform_len; +} + + +static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + const u8 *pend, *ppos; + int proposal_len, i; + const struct ikev2_proposal *p; + + if (end - pos < (int) sizeof(*p)) { + wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); + return -1; + } + + /* FIX: AND processing if multiple proposals use the same # */ + + p = (const struct ikev2_proposal *) pos; + proposal_len = WPA_GET_BE16(p->proposal_length); + if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", + proposal_len); + return -1; + } + wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", + p->proposal_num); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " + " Protocol ID: %d", + p->type, proposal_len, p->protocol_id); + wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", + p->spi_size, p->num_transforms); + + if (p->type != 0 && p->type != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); + return -1; + } + + if (p->protocol_id != IKEV2_PROTOCOL_IKE) { + wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " + "(only IKE allowed for EAP-IKEv2)"); + return -1; + } + + if (p->proposal_num != prop->proposal_num) { + if (p->proposal_num == prop->proposal_num + 1) + prop->proposal_num = p->proposal_num; + else { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); + return -1; + } + } + + ppos = (const u8 *) (p + 1); + pend = pos + proposal_len; + if (ppos + p->spi_size > pend) { + wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " + "in proposal"); + return -1; + } + if (p->spi_size) { + wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", + ppos, p->spi_size); + ppos += p->spi_size; + } + + /* + * For initial IKE_SA negotiation, SPI Size MUST be zero; for + * subsequent negotiations, it must be 8 for IKE. We only support + * initial case for now. + */ + if (p->spi_size != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); + return -1; + } + + if (p->num_transforms == 0) { + wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); + return -1; + } + + for (i = 0; i < (int) p->num_transforms; i++) { + int tlen = ikev2_parse_transform(prop, ppos, pend); + if (tlen < 0) + return -1; + ppos += tlen; + } + + if (ppos != pend) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " + "transforms"); + return -1; + } + + return proposal_len; +} + + +static int ikev2_process_sai1(struct ikev2_responder_data *data, + const u8 *sai1, size_t sai1_len) +{ + struct ikev2_proposal_data prop; + const u8 *pos, *end; + int found = 0; + + /* Security Association Payloads: <Proposals> */ + + if (sai1 == NULL) { + wpa_printf(MSG_INFO, "IKEV2: SAi1 not received"); + return -1; + } + + os_memset(&prop, 0, sizeof(prop)); + prop.proposal_num = 1; + + pos = sai1; + end = sai1 + sai1_len; + + while (pos < end) { + int plen; + + prop.integ = -1; + prop.prf = -1; + prop.encr = -1; + prop.dh = -1; + plen = ikev2_parse_proposal(&prop, pos, end); + if (plen < 0) + return -1; + + if (!found && prop.integ != -1 && prop.prf != -1 && + prop.encr != -1 && prop.dh != -1) { + os_memcpy(&data->proposal, &prop, sizeof(prop)); + data->dh = dh_groups_get(prop.dh); + found = 1; + } + + pos += plen; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals"); + return -1; + } + + if (!found) { + wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " + "INTEG:%d D-H:%d", data->proposal.proposal_num, + data->proposal.encr, data->proposal.prf, + data->proposal.integ, data->proposal.dh); + + return 0; +} + + +static int ikev2_process_kei(struct ikev2_responder_data *data, + const u8 *kei, size_t kei_len) +{ + u16 group; + + /* + * Key Exchange Payload: + * DH Group # (16 bits) + * RESERVED (16 bits) + * Key Exchange Data (Diffie-Hellman public value) + */ + + if (kei == NULL) { + wpa_printf(MSG_INFO, "IKEV2: KEi not received"); + return -1; + } + + if (kei_len < 4 + 96) { + wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + return -1; + } + + group = WPA_GET_BE16(kei); + wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group); + + if (group != data->proposal.dh) { + wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match " + "with the selected proposal (%u)", + group, data->proposal.dh); + /* Reject message with Notify payload of type + * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */ + data->error_type = INVALID_KE_PAYLOAD; + data->state = NOTIFY; + return -1; + } + + if (data->dh == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); + return -1; + } + + /* RFC 4306, Section 3.4: + * The length of DH public value MUST be equal to the lenght of the + * prime modulus. + */ + if (kei_len - 4 != data->dh->prime_len) { + wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " + "%ld (expected %ld)", + (long) (kei_len - 4), (long) data->dh->prime_len); + return -1; + } + + wpabuf_free(data->i_dh_public); + data->i_dh_public = wpabuf_alloc(kei_len - 4); + if (data->i_dh_public == NULL) + return -1; + wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4); + + wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value", + data->i_dh_public); + + return 0; +} + + +static int ikev2_process_ni(struct ikev2_responder_data *data, + const u8 *ni, size_t ni_len) +{ + if (ni == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Ni not received"); + return -1; + } + + if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld", + (long) ni_len); + return -1; + } + +#ifdef CCNS_PL + /* Zeros are removed incorrectly from the beginning of the nonces */ + while (ni_len > 1 && *ni == 0) { + ni_len--; + ni++; + } +#endif /* CCNS_PL */ + + data->i_nonce_len = ni_len; + os_memcpy(data->i_nonce, ni, ni_len); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni", + data->i_nonce, data->i_nonce_len); + + return 0; +} + + +static int ikev2_process_sa_init(struct ikev2_responder_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 || + ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 || + ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0) + return -1; + + os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN); + + return 0; +} + + +static int ikev2_process_idi(struct ikev2_responder_data *data, + const u8 *idi, size_t idi_len) +{ + u8 id_type; + + if (idi == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDi received"); + return -1; + } + + if (idi_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload"); + return -1; + } + + id_type = idi[0]; + idi += 4; + idi_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type); + wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len); + os_free(data->IDi); + data->IDi = os_malloc(idi_len); + if (data->IDi == NULL) + return -1; + os_memcpy(data->IDi, idi, idi_len); + data->IDi_len = idi_len; + data->IDi_type = id_type; + + return 0; +} + + +static int ikev2_process_cert(struct ikev2_responder_data *data, + const u8 *cert, size_t cert_len) +{ + u8 cert_encoding; + + if (cert == NULL) { + if (data->peer_auth == PEER_AUTH_CERT) { + wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); + return -1; + } + return 0; + } + + if (cert_len < 1) { + wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); + return -1; + } + + cert_encoding = cert[0]; + cert++; + cert_len--; + + wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); + + /* TODO: validate certificate */ + + return 0; +} + + +static int ikev2_process_auth_cert(struct ikev2_responder_data *data, + u8 method, const u8 *auth, size_t auth_len) +{ + if (method != AUTH_RSA_SIGN) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* TODO: validate AUTH */ + return 0; +} + + +static int ikev2_process_auth_secret(struct ikev2_responder_data *data, + u8 method, const u8 *auth, + size_t auth_len) +{ + u8 auth_data[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + + if (method != AUTH_SHARED_KEY_MIC) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* msg | Nr | prf(SK_pi,IDi') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, + data->IDi, data->IDi_len, data->IDi_type, + &data->keys, 1, data->shared_secret, + data->shared_secret_len, + data->r_nonce, data->r_nonce_len, + data->key_pad, data->key_pad_len, + auth_data) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = NULL; + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + if (auth_len != prf->hash_len || + os_memcmp(auth, auth_data, auth_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); + wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", + auth, auth_len); + wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", + auth_data, prf->hash_len); + data->error_type = AUTHENTICATION_FAILED; + data->state = NOTIFY; + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully " + "using shared keys"); + + return 0; +} + + +static int ikev2_process_auth(struct ikev2_responder_data *data, + const u8 *auth, size_t auth_len) +{ + u8 auth_method; + + if (auth == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); + return -1; + } + + if (auth_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " + "Payload"); + return -1; + } + + auth_method = auth[0]; + auth += 4; + auth_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); + + switch (data->peer_auth) { + case PEER_AUTH_CERT: + return ikev2_process_auth_cert(data, auth_method, auth, + auth_len); + case PEER_AUTH_SECRET: + return ikev2_process_auth_secret(data, auth_method, auth, + auth_len); + } + + return -1; +} + + +static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data, + u8 next_payload, + u8 *payload, size_t payload_len) +{ + struct ikev2_payloads pl; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, payload, payload + + payload_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 || + ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || + ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) + return -1; + + return 0; +} + + +static int ikev2_process_sa_auth(struct ikev2_responder_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + u8 *decrypted; + size_t decrypted_len; + int ret; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, + &data->keys, 1, hdr, pl->encrypted, + pl->encrypted_len, &decrypted_len); + if (decrypted == NULL) + return -1; + + ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, + decrypted, decrypted_len); + os_free(decrypted); + + return ret; +} + + +static int ikev2_validate_rx_state(struct ikev2_responder_data *data, + u8 exchange_type, u32 message_id) +{ + switch (data->state) { + case SA_INIT: + /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */ + if (exchange_type != IKE_SA_INIT) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_INIT state", exchange_type); + return -1; + } + if (message_id != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_INIT state", message_id); + return -1; + } + break; + case SA_AUTH: + /* Expect to receive IKE_SA_AUTH: + * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,] + * AUTH, SAi2, TSi, TSr} + */ + if (exchange_type != IKE_SA_AUTH) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_AUTH state", exchange_type); + return -1; + } + if (message_id != 1) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_AUTH state", message_id); + return -1; + } + break; + case CHILD_SA: + if (exchange_type != CREATE_CHILD_SA) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in CHILD_SA state", exchange_type); + return -1; + } + if (message_id != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in CHILD_SA state", message_id); + return -1; + } + break; + case NOTIFY: + case IKEV2_DONE: + case IKEV2_FAILED: + return -1; + } + + return 0; +} + + +int ikev2_responder_process(struct ikev2_responder_data *data, + const struct wpabuf *buf) +{ + const struct ikev2_hdr *hdr; + u32 length, message_id; + const u8 *pos, *end; + struct ikev2_payloads pl; + + wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", + (unsigned long) wpabuf_len(buf)); + + if (wpabuf_len(buf) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); + return -1; + } + + data->error_type = 0; + hdr = (const struct ikev2_hdr *) wpabuf_head(buf); + end = wpabuf_head_u8(buf) + wpabuf_len(buf); + message_id = WPA_GET_BE32(hdr->message_id); + length = WPA_GET_BE32(hdr->length); + + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->i_spi, IKEV2_SPI_LEN); + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", + hdr->r_spi, IKEV2_SPI_LEN); + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " + "Exchange Type: %u", + hdr->next_payload, hdr->version, hdr->exchange_type); + wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", + message_id, length); + + if (hdr->version != IKEV2_VERSION) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " + "(expected 0x%x)", hdr->version, IKEV2_VERSION); + return -1; + } + + if (length != wpabuf_len(buf)) { + wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " + "RX: %lu)", (unsigned long) length, + (unsigned long) wpabuf_len(buf)); + return -1; + } + + if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) + return -1; + + if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != + IKEV2_HDR_INITIATOR) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", + hdr->flags); + return -1; + } + + if (data->state != SA_INIT) { + if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Initiator's SPI"); + return -1; + } + if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Responder's SPI"); + return -1; + } + } + + pos = (const u8 *) (hdr + 1); + if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) + return -1; + + if (data->state == SA_INIT) { + data->last_msg = LAST_MSG_SA_INIT; + if (ikev2_process_sa_init(data, hdr, &pl) < 0) { + if (data->state == NOTIFY) + return 0; + return -1; + } + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = wpabuf_dup(buf); + } + + if (data->state == SA_AUTH) { + data->last_msg = LAST_MSG_SA_AUTH; + if (ikev2_process_sa_auth(data, hdr, &pl) < 0) { + if (data->state == NOTIFY) + return 0; + return -1; + } + } + + return 0; +} + + +static void ikev2_build_hdr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 exchange_type, + u8 next_payload, u32 message_id) +{ + struct ikev2_hdr *hdr; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); + + /* HDR - RFC 4306, Sect. 3.1 */ + hdr = wpabuf_put(msg, sizeof(*hdr)); + os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); + os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); + hdr->next_payload = next_payload; + hdr->version = IKEV2_VERSION; + hdr->exchange_type = exchange_type; + hdr->flags = IKEV2_HDR_RESPONSE; + WPA_PUT_BE32(hdr->message_id, message_id); +} + + +static int ikev2_build_sar1(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct ikev2_proposal *p; + struct ikev2_transform *t; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload"); + + /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + p = wpabuf_put(msg, sizeof(*p)); +#ifdef CCNS_PL + /* Seems to require that the Proposal # is 1 even though RFC 4306 + * Sect 3.3.1 has following requirement "When a proposal is accepted, + * all of the proposal numbers in the SA payload MUST be the same and + * MUST match the number on the proposal sent that was accepted.". + */ + p->proposal_num = 1; +#else /* CCNS_PL */ + p->proposal_num = data->proposal.proposal_num; +#endif /* CCNS_PL */ + p->protocol_id = IKEV2_PROTOCOL_IKE; + p->num_transforms = 4; + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + t->transform_type = IKEV2_TRANSFORM_ENCR; + WPA_PUT_BE16(t->transform_id, data->proposal.encr); + if (data->proposal.encr == ENCR_AES_CBC) { + /* Transform Attribute: Key Len = 128 bits */ +#ifdef CCNS_PL + wpabuf_put_be16(msg, 0x001d); /* ?? */ +#else /* CCNS_PL */ + wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ +#endif /* CCNS_PL */ + wpabuf_put_be16(msg, 128); /* 128-bit key */ + } + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; + WPA_PUT_BE16(t->transform_length, plen); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_PRF; + WPA_PUT_BE16(t->transform_id, data->proposal.prf); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_INTEG; + WPA_PUT_BE16(t->transform_id, data->proposal.integ); + + t = wpabuf_put(msg, sizeof(*t)); + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_DH; + WPA_PUT_BE16(t->transform_id, data->proposal.dh); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; + WPA_PUT_BE16(p->proposal_length, plen); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + return 0; +} + + +static int ikev2_build_ker(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct wpabuf *pv; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload"); + + pv = dh_init(data->dh, &data->r_dh_private); + if (pv == NULL) { + wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); + return -1; + } + + /* KEr - RFC 4306, Sect. 3.4 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ + wpabuf_put(msg, 2); /* RESERVED */ + /* + * RFC 4306, Sect. 3.4: possible zero padding for public value to + * match the length of the prime. + */ + wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); + wpabuf_put_buf(msg, pv); + wpabuf_free(pv); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_nr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload"); + + /* Nr - RFC 4306, Sect. 3.9 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_idr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload"); + + if (data->IDr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDr available"); + return -1; + } + + /* IDr - RFC 4306, Sect. 3.5 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, ID_KEY_ID); + wpabuf_put(msg, 3); /* RESERVED */ + wpabuf_put_data(msg, data->IDr, data->IDr_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_auth(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + const struct ikev2_prf_alg *prf; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + /* Authentication - RFC 4306, Sect. 3.8 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); + wpabuf_put(msg, 3); /* RESERVED */ + + /* msg | Ni | prf(SK_pr,IDr') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, + data->IDr, data->IDr_len, ID_KEY_ID, + &data->keys, 0, data->shared_secret, + data->shared_secret_len, + data->i_nonce, data->i_nonce_len, + data->key_pad, data->key_pad_len, + wpabuf_put(msg, prf->hash_len)) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = NULL; + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_notification(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload"); + + if (data->error_type == 0) { + wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type " + "available"); + return -1; + } + + /* Notify - RFC 4306, Sect. 3.10 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; +#ifdef CCNS_PL + wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */ +#else /* CCNS_PL */ + wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */ +#endif /* CCNS_PL */ + wpabuf_put_u8(msg, 0); /* SPI Size */ + wpabuf_put_be16(msg, data->error_type); + + switch (data->error_type) { + case INVALID_KE_PAYLOAD: + if (data->proposal.dh == -1) { + wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for " + "INVALID_KE_PAYLOAD notifications"); + return -1; + } + wpabuf_put_be16(msg, data->proposal.dh); + wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request " + "DH Group #%d", data->proposal.dh); + break; + case AUTHENTICATION_FAILED: + /* no associated data */ + break; + default: + wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type " + "%d", data->error_type); + return -1; + } + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) +{ + struct wpabuf *msg; + + /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */ + + if (os_get_random(data->r_spi, IKEV2_SPI_LEN)) + return NULL; + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", + data->r_spi, IKEV2_SPI_LEN); + + data->r_nonce_len = IKEV2_NONCE_MIN_LEN; + if (os_get_random(data->r_nonce, data->r_nonce_len)) + return NULL; +#ifdef CCNS_PL + /* Zeros are removed incorrectly from the beginning of the nonces in + * key derivation; as a workaround, make sure Nr does not start with + * zero.. */ + if (data->r_nonce[0] == 0) + data->r_nonce[0] = 1; +#endif /* CCNS_PL */ + wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500); + if (msg == NULL) + return NULL; + + ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); + if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || + ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) || + ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ? + IKEV2_PAYLOAD_ENCRYPTED : + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_derive_keys(data)) { + wpabuf_free(msg); + return NULL; + } + + if (data->peer_auth == PEER_AUTH_CERT) { + /* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info + * for trust agents */ + } + + if (data->peer_auth == PEER_AUTH_SECRET) { + struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + if (ikev2_build_idr(data, plain, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_IDr)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); + + data->state = SA_AUTH; + + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = wpabuf_dup(msg); + + return msg; +} + + +static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data) +{ + struct wpabuf *msg, *plain; + + /* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */ + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); + if (msg == NULL) + return NULL; + ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); + + plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || + ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_IDr)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); + + data->state = IKEV2_DONE; + + return msg; +} + + +static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); + if (msg == NULL) + return NULL; + if (data->last_msg == LAST_MSG_SA_AUTH) { + /* HDR, SK{N} */ + struct wpabuf *plain = wpabuf_alloc(100); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + ikev2_build_hdr(data, msg, IKE_SA_AUTH, + IKEV2_PAYLOAD_ENCRYPTED, 1); + if (ikev2_build_notification(data, plain, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_NOTIFICATION)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + data->state = IKEV2_FAILED; + } else { + /* HDR, N */ + ikev2_build_hdr(data, msg, IKE_SA_INIT, + IKEV2_PAYLOAD_NOTIFICATION, 0); + if (ikev2_build_notification(data, msg, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + data->state = SA_INIT; + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)", + msg); + + return msg; +} + + +struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data) +{ + switch (data->state) { + case SA_INIT: + return ikev2_build_sa_init(data); + case SA_AUTH: + return ikev2_build_sa_auth(data); + case CHILD_SA: + return NULL; + case NOTIFY: + return ikev2_build_notify(data); + case IKEV2_DONE: + case IKEV2_FAILED: + return NULL; + } + return NULL; +} diff --git a/src/eap_peer/ikev2.h b/src/eap_peer/ikev2.h new file mode 100644 index 0000000000000..9ca0ca56959d1 --- /dev/null +++ b/src/eap_peer/ikev2.h @@ -0,0 +1,65 @@ +/* + * IKEv2 responder (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IKEV2_H +#define IKEV2_H + +#include "eap_common/ikev2_common.h" + +struct ikev2_proposal_data { + u8 proposal_num; + int integ; + int prf; + int encr; + int dh; +}; + + +struct ikev2_responder_data { + enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED } + state; + u8 i_spi[IKEV2_SPI_LEN]; + u8 r_spi[IKEV2_SPI_LEN]; + u8 i_nonce[IKEV2_NONCE_MAX_LEN]; + size_t i_nonce_len; + u8 r_nonce[IKEV2_NONCE_MAX_LEN]; + size_t r_nonce_len; + struct wpabuf *i_dh_public; + struct wpabuf *r_dh_private; + struct ikev2_proposal_data proposal; + const struct dh_group *dh; + struct ikev2_keys keys; + u8 *IDi; + size_t IDi_len; + u8 IDi_type; + u8 *IDr; + size_t IDr_len; + struct wpabuf *r_sign_msg; + struct wpabuf *i_sign_msg; + u8 *shared_secret; + size_t shared_secret_len; + enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; + u8 *key_pad; + size_t key_pad_len; + u16 error_type; + enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg; +}; + + +void ikev2_responder_deinit(struct ikev2_responder_data *data); +int ikev2_responder_process(struct ikev2_responder_data *data, + const struct wpabuf *buf); +struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data); + +#endif /* IKEV2_H */ diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c new file mode 100644 index 0000000000000..01c22d89753f9 --- /dev/null +++ b/src/eap_peer/mschapv2.c @@ -0,0 +1,119 @@ +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ms_funcs.h" +#include "mschapv2.h" + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) +{ + size_t i; + + /* + * MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). + */ + + for (i = 0; i < *len; i++) { + if (username[i] == '\\') { + *len -= i + 1; + return username + i + 1; + } + } + + return username; +} + + +void mschapv2_derive_response(const u8 *identity, size_t identity_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key) +{ + const u8 *username; + size_t username_len; + u8 password_hash[16], password_hash_hash[16]; + + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", + identity, identity_len); + username_len = identity_len; + username = mschapv2_remove_domain(identity, &username_len); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", + username, username_len); + + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", + auth_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", + peer_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", + username, username_len); + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + if (pwhash) { + wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", + password, password_len); + generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response); + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, auth_response); + } else { + wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", + password, password_len); + generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, nt_response); + generate_authenticator_response(password, password_len, + peer_challenge, auth_challenge, + username, username_len, + nt_response, auth_response); + } + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", + nt_response, MSCHAPV2_NT_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", + auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); + + /* Generate master_key here since we have the needed data available. */ + if (pwhash) { + hash_nt_password_hash(password, password_hash_hash); + } else { + nt_password_hash(password, password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + } + get_master_key(password_hash_hash, nt_response, master_key); + wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", + master_key, MSCHAPV2_MASTER_KEY_LEN); +} + + +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len) +{ + u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN || + buf[0] != 'S' || buf[1] != '=' || + hexstr2bin((char *) (buf + 2), recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) || + os_memcmp(auth_response, recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) != 0) + return -1; + return 0; +} diff --git a/src/eap_peer/mschapv2.h b/src/eap_peer/mschapv2.h new file mode 100644 index 0000000000000..c7c36f7721787 --- /dev/null +++ b/src/eap_peer/mschapv2.h @@ -0,0 +1,34 @@ +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MSCHAPV2_H +#define MSCHAPV2_H + +#define MSCHAPV2_CHAL_LEN 16 +#define MSCHAPV2_NT_RESPONSE_LEN 24 +#define MSCHAPV2_AUTH_RESPONSE_LEN 20 +#define MSCHAPV2_MASTER_KEY_LEN 16 + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); +void mschapv2_derive_response(const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key); +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len); + +#endif /* MSCHAPV2_H */ diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c new file mode 100644 index 0000000000000..662662d296ec9 --- /dev/null +++ b/src/eap_peer/tncc.c @@ -0,0 +1,1368 @@ +/* + * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <dlfcn.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "base64.h" +#include "tncc.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_defs.h" + + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +#define TNC_CONFIG_FILE "/etc/tnc_config" +#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") +#define IF_TNCCS_START \ +"<?xml version=\"1.0\"?>\n" \ +"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \ +"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \ +"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \ +"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \ +"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n" +#define IF_TNCCS_END "\n</TNCCS-Batch>" + +/* TNC IF-IMC */ + +typedef unsigned long TNC_UInt32; +typedef unsigned char *TNC_BufferReference; + +typedef TNC_UInt32 TNC_IMCID; +typedef TNC_UInt32 TNC_ConnectionID; +typedef TNC_UInt32 TNC_ConnectionState; +typedef TNC_UInt32 TNC_RetryReason; +typedef TNC_UInt32 TNC_MessageType; +typedef TNC_MessageType *TNC_MessageTypeList; +typedef TNC_UInt32 TNC_VendorID; +typedef TNC_UInt32 TNC_MessageSubtype; +typedef TNC_UInt32 TNC_Version; +typedef TNC_UInt32 TNC_Result; + +typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( + TNC_IMCID imcID, + char *functionName, + void **pOutfunctionPointer); + +#define TNC_RESULT_SUCCESS 0 +#define TNC_RESULT_NOT_INITIALIZED 1 +#define TNC_RESULT_ALREADY_INITIALIZED 2 +#define TNC_RESULT_NO_COMMON_VERSION 3 +#define TNC_RESULT_CANT_RETRY 4 +#define TNC_RESULT_WONT_RETRY 5 +#define TNC_RESULT_INVALID_PARAMETER 6 +#define TNC_RESULT_CANT_RESPOND 7 +#define TNC_RESULT_ILLEGAL_OPERATION 8 +#define TNC_RESULT_OTHER 9 +#define TNC_RESULT_FATAL 10 + +#define TNC_CONNECTION_STATE_CREATE 0 +#define TNC_CONNECTION_STATE_HANDSHAKE 1 +#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 +#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 +#define TNC_CONNECTION_STATE_ACCESS_NONE 4 +#define TNC_CONNECTION_STATE_DELETE 5 + +#define TNC_IFIMC_VERSION_1 1 + +#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) +#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) + +/* TNCC-TNCS Message Types */ +#define TNC_TNCCS_RECOMMENDATION 0x00000001 +#define TNC_TNCCS_ERROR 0x00000002 +#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 +#define TNC_TNCCS_REASONSTRINGS 0x00000004 + + +/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ +enum { + SSOH_MS_MACHINE_INVENTORY = 1, + SSOH_MS_QUARANTINE_STATE = 2, + SSOH_MS_PACKET_INFO = 3, + SSOH_MS_SYSTEMGENERATED_IDS = 4, + SSOH_MS_MACHINENAME = 5, + SSOH_MS_CORRELATIONID = 6, + SSOH_MS_INSTALLED_SHVS = 7, + SSOH_MS_MACHINE_INVENTORY_EX = 8 +}; + +struct tnc_if_imc { + struct tnc_if_imc *next; + char *name; + char *path; + void *dlhandle; /* from dlopen() */ + TNC_IMCID imcID; + TNC_ConnectionID connectionID; + TNC_MessageTypeList supported_types; + size_t num_supported_types; + u8 *imc_send; + size_t imc_send_len; + + /* Functions implemented by IMCs (with TNC_IMC_ prefix) */ + TNC_Result (*Initialize)( + TNC_IMCID imcID, + TNC_Version minVersion, + TNC_Version maxVersion, + TNC_Version *pOutActualVersion); + TNC_Result (*NotifyConnectionChange)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_ConnectionState newState); + TNC_Result (*BeginHandshake)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID); + TNC_Result (*ReceiveMessage)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference messageBuffer, + TNC_UInt32 messageLength, + TNC_MessageType messageType); + TNC_Result (*BatchEnding)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID); + TNC_Result (*Terminate)(TNC_IMCID imcID); + TNC_Result (*ProvideBindFunction)( + TNC_IMCID imcID, + TNC_TNCC_BindFunctionPointer bindFunction); +}; + +struct tncc_data { + struct tnc_if_imc *imc; + unsigned int last_batchid; +}; + +#define TNC_MAX_IMC_ID 10 +static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; + + +/* TNCC functions that IMCs can call */ + +TNC_Result TNC_TNCC_ReportMessageTypes( + TNC_IMCID imcID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount) +{ + TNC_UInt32 i; + struct tnc_if_imc *imc; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu " + "typeCount=%lu)", + (unsigned long) imcID, (unsigned long) typeCount); + + for (i = 0; i < typeCount; i++) { + wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", + i, supportedTypes[i]); + } + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + imc = tnc_imc[imcID]; + os_free(imc->supported_types); + imc->supported_types = + os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + if (imc->supported_types == NULL) + return TNC_RESULT_FATAL; + os_memcpy(imc->supported_types, supportedTypes, + typeCount * sizeof(TNC_MessageTypeList)); + imc->num_supported_types = typeCount; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_SendMessage( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType) +{ + struct tnc_if_imc *imc; + unsigned char *b64; + size_t b64len; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu " + "connectionID=%lu messageType=%lu)", + imcID, connectionID, messageType); + wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage", + message, messageLength); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + b64 = base64_encode(message, messageLength, &b64len); + if (b64 == NULL) + return TNC_RESULT_FATAL; + + imc = tnc_imc[imcID]; + os_free(imc->imc_send); + imc->imc_send_len = 0; + imc->imc_send = os_zalloc(b64len + 100); + if (imc->imc_send == NULL) { + os_free(b64); + return TNC_RESULT_OTHER; + } + + imc->imc_send_len = + os_snprintf((char *) imc->imc_send, b64len + 100, + "<IMC-IMV-Message><Type>%08X</Type>" + "<Base64>%s</Base64></IMC-IMV-Message>", + (unsigned int) messageType, b64); + + os_free(b64); + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_RequestHandshakeRetry( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry"); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + /* + * TODO: trigger a call to eapol_sm_request_reauth(). This would + * require that the IMC continues to be loaded in memory afer + * authentication.. + */ + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, + const char *message) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " + "severity==%lu message='%s')", + imcID, severity, message); + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, + const char *message) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " + "connectionID==%lu message='%s')", + imcID, connectionID, message); + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_BindFunction( + TNC_IMCID imcID, + char *functionName, + void **pOutfunctionPointer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, " + "functionName='%s')", (unsigned long) imcID, functionName); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (pOutfunctionPointer == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0) + *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes; + else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0) + *pOutfunctionPointer = TNC_TNCC_SendMessage; + else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") == + 0) + *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry; + else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0) + *pOutfunctionPointer = TNC_9048_LogMessage; + else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0) + *pOutfunctionPointer = TNC_9048_UserMessage; + else + *pOutfunctionPointer = NULL; + + return TNC_RESULT_SUCCESS; +} + + +static void * tncc_get_sym(void *handle, char *func) +{ + void *fptr; + +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef _WIN32_WCE + fptr = GetProcAddressA(handle, func); +#else /* _WIN32_WCE */ + fptr = GetProcAddress(handle, func); +#endif /* _WIN32_WCE */ +#else /* CONFIG_NATIVE_WINDOWS */ + fptr = dlsym(handle, func); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return fptr; +} + + +static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc) +{ + void *handle = imc->dlhandle; + + /* Mandatory IMC functions */ + imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize"); + if (imc->Initialize == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_Initialize"); + return -1; + } + + imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake"); + if (imc->BeginHandshake == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_BeginHandshake"); + return -1; + } + + imc->ProvideBindFunction = + tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction"); + if (imc->ProvideBindFunction == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_ProvideBindFunction"); + return -1; + } + + /* Optional IMC functions */ + imc->NotifyConnectionChange = + tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange"); + imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage"); + imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding"); + imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate"); + + return 0; +} + + +static int tncc_imc_initialize(struct tnc_if_imc *imc) +{ + TNC_Result res; + TNC_Version imc_ver; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'", + imc->name); + res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1, + TNC_IFIMC_VERSION_1, &imc_ver); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu", + (unsigned long) res, (unsigned long) imc_ver); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_terminate(struct tnc_if_imc *imc) +{ + TNC_Result res; + + if (imc->Terminate == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'", + imc->name); + res = imc->Terminate(imc->imcID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for " + "IMC '%s'", imc->name); + res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc, + TNC_ConnectionState state) +{ + TNC_Result res; + + if (imc->NotifyConnectionChange == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)" + " for IMC '%s'", (int) state, imc->name); + res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID, + state); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_begin_handshake(struct tnc_if_imc *imc) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC " + "'%s'", imc->name); + res = imc->BeginHandshake(imc->imcID, imc->connectionID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_load_imc(struct tnc_if_imc *imc) +{ + if (imc->path == NULL) { + wpa_printf(MSG_DEBUG, "TNC: No IMC configured"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)", + imc->name, imc->path); +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef UNICODE + { + TCHAR *lib = wpa_strdup_tchar(imc->path); + if (lib == NULL) + return -1; + imc->dlhandle = LoadLibrary(lib); + os_free(lib); + } +#else /* UNICODE */ + imc->dlhandle = LoadLibrary(imc->path); +#endif /* UNICODE */ + if (imc->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d", + imc->name, imc->path, (int) GetLastError()); + return -1; + } +#else /* CONFIG_NATIVE_WINDOWS */ + imc->dlhandle = dlopen(imc->path, RTLD_LAZY); + if (imc->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s", + imc->name, imc->path, dlerror()); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (tncc_imc_resolve_funcs(imc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions"); + return -1; + } + + if (tncc_imc_initialize(imc) < 0 || + tncc_imc_provide_bind_function(imc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC"); + return -1; + } + + return 0; +} + + +static void tncc_unload_imc(struct tnc_if_imc *imc) +{ + tncc_imc_terminate(imc); + tnc_imc[imc->imcID] = NULL; + + if (imc->dlhandle) { +#ifdef CONFIG_NATIVE_WINDOWS + FreeLibrary(imc->dlhandle); +#else /* CONFIG_NATIVE_WINDOWS */ + dlclose(imc->dlhandle); +#endif /* CONFIG_NATIVE_WINDOWS */ + } + os_free(imc->name); + os_free(imc->path); + os_free(imc->supported_types); + os_free(imc->imc_send); +} + + +static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type) +{ + size_t i; + unsigned int vendor, subtype; + + if (imc == NULL || imc->supported_types == NULL) + return 0; + + vendor = type >> 8; + subtype = type & 0xff; + + for (i = 0; i < imc->num_supported_types; i++) { + unsigned int svendor, ssubtype; + svendor = imc->supported_types[i] >> 8; + ssubtype = imc->supported_types[i] & 0xff; + if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && + (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) + return 1; + } + + return 0; +} + + +static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type, + const u8 *msg, size_t len) +{ + struct tnc_if_imc *imc; + TNC_Result res; + + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len); + + for (imc = tncc->imc; imc; imc = imc->next) { + if (imc->ReceiveMessage == NULL || + !tncc_supported_type(imc, type)) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'", + imc->name); + res = imc->ReceiveMessage(imc->imcID, imc->connectionID, + (TNC_BufferReference) msg, len, + type); + wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", + (unsigned long) res); + } +} + + +void tncc_init_connection(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc; + + for (imc = tncc->imc; imc; imc = imc->next) { + tncc_imc_notify_connection_change( + imc, TNC_CONNECTION_STATE_CREATE); + tncc_imc_notify_connection_change( + imc, TNC_CONNECTION_STATE_HANDSHAKE); + + os_free(imc->imc_send); + imc->imc_send = NULL; + imc->imc_send_len = 0; + + tncc_imc_begin_handshake(imc); + } +} + + +size_t tncc_total_send_len(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc; + + size_t len = 0; + for (imc = tncc->imc; imc; imc = imc->next) + len += imc->imc_send_len; + return len; +} + + +u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos) +{ + struct tnc_if_imc *imc; + + for (imc = tncc->imc; imc; imc = imc->next) { + if (imc->imc_send == NULL) + continue; + + os_memcpy(pos, imc->imc_send, imc->imc_send_len); + pos += imc->imc_send_len; + os_free(imc->imc_send); + imc->imc_send = NULL; + imc->imc_send_len = 0; + } + + return pos; +} + + +char * tncc_if_tnccs_start(struct tncc_data *tncc) +{ + char *buf = os_malloc(1000); + if (buf == NULL) + return NULL; + tncc->last_batchid++; + os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid); + return buf; +} + + +char * tncc_if_tnccs_end(void) +{ + char *buf = os_malloc(100); + if (buf == NULL) + return NULL; + os_snprintf(buf, 100, IF_TNCCS_END); + return buf; +} + + +static void tncc_notify_recommendation(struct tncc_data *tncc, + enum tncc_process_res res) +{ + TNC_ConnectionState state; + struct tnc_if_imc *imc; + + switch (res) { + case TNCCS_RECOMMENDATION_ALLOW: + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + case TNCCS_RECOMMENDATION_NONE: + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; + break; + default: + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + } + + for (imc = tncc->imc; imc; imc = imc->next) + tncc_imc_notify_connection_change(imc, state); +} + + +static int tncc_get_type(char *start, unsigned int *type) +{ + char *pos = os_strstr(start, "<Type>"); + if (pos == NULL) + return -1; + pos += 6; + *type = strtoul(pos, NULL, 16); + return 0; +} + + +static unsigned char * tncc_get_base64(char *start, size_t *decoded_len) +{ + char *pos, *pos2; + unsigned char *decoded; + + pos = os_strstr(start, "<Base64>"); + if (pos == NULL) + return NULL; + + pos += 8; + pos2 = os_strstr(pos, "</Base64>"); + if (pos2 == NULL) + return NULL; + *pos2 = '\0'; + + decoded = base64_decode((unsigned char *) pos, os_strlen(pos), + decoded_len); + *pos2 = '<'; + if (decoded == NULL) { + wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); + } + + return decoded; +} + + +static enum tncc_process_res tncc_get_recommendation(char *start) +{ + char *pos, *pos2, saved; + int recom; + + pos = os_strstr(start, "<TNCCS-Recommendation "); + if (pos == NULL) + return TNCCS_RECOMMENDATION_ERROR; + + pos += 21; + pos = os_strstr(pos, " type="); + if (pos == NULL) + return TNCCS_RECOMMENDATION_ERROR; + pos += 6; + + if (*pos == '"') + pos++; + + pos2 = pos; + while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>') + pos2++; + + if (*pos2 == '\0') + return TNCCS_RECOMMENDATION_ERROR; + + saved = *pos2; + *pos2 = '\0'; + wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos); + + recom = TNCCS_RECOMMENDATION_ERROR; + if (os_strcmp(pos, "allow") == 0) + recom = TNCCS_RECOMMENDATION_ALLOW; + else if (os_strcmp(pos, "none") == 0) + recom = TNCCS_RECOMMENDATION_NONE; + else if (os_strcmp(pos, "isolate") == 0) + recom = TNCCS_RECOMMENDATION_ISOLATE; + + *pos2 = saved; + + return recom; +} + + +enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, + const u8 *msg, size_t len) +{ + char *buf, *start, *end, *pos, *pos2, *payload; + unsigned int batch_id; + unsigned char *decoded; + size_t decoded_len; + enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; + int recommendation_msg = 0; + + buf = os_malloc(len + 1); + if (buf == NULL) + return TNCCS_PROCESS_ERROR; + + os_memcpy(buf, msg, len); + buf[len] = '\0'; + start = os_strstr(buf, "<TNCCS-Batch "); + end = os_strstr(buf, "</TNCCS-Batch>"); + if (start == NULL || end == NULL || start > end) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + start += 13; + while (*start == ' ') + start++; + *end = '\0'; + + pos = os_strstr(start, "BatchId="); + if (pos == NULL) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + pos += 8; + if (*pos == '"') + pos++; + batch_id = atoi(pos); + wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", + batch_id); + if (batch_id != tncc->last_batchid + 1) { + wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " + "%u (expected %u)", + batch_id, tncc->last_batchid + 1); + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + tncc->last_batchid = batch_id; + + while (*pos != '\0' && *pos != '>') + pos++; + if (*pos == '\0') { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + pos++; + payload = start; + + /* + * <IMC-IMV-Message> + * <Type>01234567</Type> + * <Base64>foo==</Base64> + * </IMC-IMV-Message> + */ + + while (*start) { + char *endpos; + unsigned int type; + + pos = os_strstr(start, "<IMC-IMV-Message>"); + if (pos == NULL) + break; + start = pos + 17; + end = os_strstr(start, "</IMC-IMV-Message>"); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 18; + + if (tncc_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); + + decoded = tncc_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + + tncc_send_to_imcs(tncc, type, decoded, decoded_len); + + os_free(decoded); + + start = end; + } + + /* + * <TNCC-TNCS-Message> + * <Type>01234567</Type> + * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML> + * <Base64>foo==</Base64> + * </TNCC-TNCS-Message> + */ + + start = payload; + while (*start) { + unsigned int type; + char *xml, *xmlend, *endpos; + + pos = os_strstr(start, "<TNCC-TNCS-Message>"); + if (pos == NULL) + break; + start = pos + 19; + end = os_strstr(start, "</TNCC-TNCS-Message>"); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 20; + + if (tncc_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", + type); + + /* Base64 OR XML */ + decoded = NULL; + xml = NULL; + xmlend = NULL; + pos = os_strstr(start, "<XML>"); + if (pos) { + pos += 5; + pos2 = os_strstr(pos, "</XML>"); + if (pos2 == NULL) { + *endpos = '<'; + start = end; + continue; + } + xmlend = pos2; + xml = pos; + } else { + decoded = tncc_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + } + + if (decoded) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message Base64", + decoded, decoded_len); + os_free(decoded); + } + + if (xml) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message XML", + (unsigned char *) xml, + xmlend - xml); + } + + if (type == TNC_TNCCS_RECOMMENDATION && xml) { + /* + * <TNCCS-Recommendation type="allow"> + * </TNCCS-Recommendation> + */ + *xmlend = '\0'; + res = tncc_get_recommendation(xml); + *xmlend = '<'; + recommendation_msg = 1; + } + + start = end; + } + + os_free(buf); + + if (recommendation_msg) + tncc_notify_recommendation(tncc, res); + + return res; +} + + +#ifdef CONFIG_NATIVE_WINDOWS +static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive) +{ + HKEY hk, hk2; + LONG ret; + DWORD i; + struct tnc_if_imc *imc, *last; + int j; + + last = tncc->imc; + while (last && last->next) + last = last->next; + + ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS, + &hk); + if (ret != ERROR_SUCCESS) + return 0; + + for (i = 0; ; i++) { + TCHAR name[255], *val; + DWORD namelen, buflen; + + namelen = 255; + ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name); + + ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR + "'", name); + continue; + } + + ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL, + &buflen); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "TNC: Could not read Path from " + "IMC key '" TSTR "'", name); + RegCloseKey(hk2); + continue; + } + + val = os_malloc(buflen); + if (val == NULL) { + RegCloseKey(hk2); + continue; + } + + ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, + (LPBYTE) val, &buflen); + if (ret != ERROR_SUCCESS) { + os_free(val); + RegCloseKey(hk2); + continue; + } + + RegCloseKey(hk2); + + wpa_unicode2ascii_inplace(val); + wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val); + + for (j = 0; j < TNC_MAX_IMC_ID; j++) { + if (tnc_imc[j] == NULL) + break; + } + if (j >= TNC_MAX_IMC_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); + os_free(val); + continue; + } + + imc = os_zalloc(sizeof(*imc)); + if (imc == NULL) { + os_free(val); + break; + } + + imc->imcID = j; + + wpa_unicode2ascii_inplace(name); + imc->name = os_strdup((char *) name); + imc->path = os_strdup((char *) val); + + os_free(val); + + if (last == NULL) + tncc->imc = imc; + else + last->next = imc; + last = imc; + + tnc_imc[imc->imcID] = imc; + } + + RegCloseKey(hk); + + return 0; +} + + +static int tncc_read_config(struct tncc_data *tncc) +{ + if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 || + tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0) + return -1; + return 0; +} + +#else /* CONFIG_NATIVE_WINDOWS */ + +static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) +{ + struct tnc_if_imc *imc; + char *pos, *pos2; + int i; + + for (i = 0; i < TNC_MAX_IMC_ID; i++) { + if (tnc_imc[i] == NULL) + break; + } + if (i >= TNC_MAX_IMC_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); + return NULL; + } + + imc = os_zalloc(sizeof(*imc)); + if (imc == NULL) { + *error = 1; + return NULL; + } + + imc->imcID = i; + + pos = start; + wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos); + if (pos + 1 >= end || *pos != '"') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no starting quotation mark)", start); + os_free(imc); + return NULL; + } + + pos++; + pos2 = pos; + while (pos2 < end && *pos2 != '"') + pos2++; + if (pos2 >= end) { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no ending quotation mark)", start); + os_free(imc); + return NULL; + } + *pos2 = '\0'; + wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); + imc->name = os_strdup(pos); + + pos = pos2 + 1; + if (pos >= end || *pos != ' ') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no space after name)", start); + os_free(imc); + return NULL; + } + + pos++; + wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos); + imc->path = os_strdup(pos); + tnc_imc[imc->imcID] = imc; + + return imc; +} + + +static int tncc_read_config(struct tncc_data *tncc) +{ + char *config, *end, *pos, *line_end; + size_t config_len; + struct tnc_if_imc *imc, *last; + + last = NULL; + + config = os_readfile(TNC_CONFIG_FILE, &config_len); + if (config == NULL) { + wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " + "file '%s'", TNC_CONFIG_FILE); + return -1; + } + + end = config + config_len; + for (pos = config; pos < end; pos = line_end + 1) { + line_end = pos; + while (*line_end != '\n' && *line_end != '\r' && + line_end < end) + line_end++; + *line_end = '\0'; + + if (os_strncmp(pos, "IMC ", 4) == 0) { + int error = 0; + + imc = tncc_parse_imc(pos + 4, line_end, &error); + if (error) + return -1; + if (imc) { + if (last == NULL) + tncc->imc = imc; + else + last->next = imc; + last = imc; + } + } + } + + os_free(config); + + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +struct tncc_data * tncc_init(void) +{ + struct tncc_data *tncc; + struct tnc_if_imc *imc; + + tncc = os_zalloc(sizeof(*tncc)); + if (tncc == NULL) + return NULL; + + /* TODO: + * move loading and Initialize() to a location that is not + * re-initialized for every EAP-TNC session (?) + */ + + if (tncc_read_config(tncc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); + goto failed; + } + + for (imc = tncc->imc; imc; imc = imc->next) { + if (tncc_load_imc(imc)) { + wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'", + imc->name); + goto failed; + } + } + + return tncc; + +failed: + tncc_deinit(tncc); + return NULL; +} + + +void tncc_deinit(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc, *prev; + + imc = tncc->imc; + while (imc) { + tncc_unload_imc(imc); + + prev = imc; + imc = imc->next; + os_free(prev); + } + + os_free(tncc); +} + + +static struct wpabuf * tncc_build_soh(int ver) +{ + struct wpabuf *buf; + u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end; + u8 correlation_id[24]; + /* TODO: get correct name */ + char *machinename = "wpa_supplicant@w1.fi"; + + if (os_get_random(correlation_id, sizeof(correlation_id))) + return NULL; + wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID", + correlation_id, sizeof(correlation_id)); + + buf = wpabuf_alloc(200); + if (buf == NULL) + return NULL; + + /* Vendor-Specific TLV (Microsoft) - SoH */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ + tlv_len = wpabuf_put(buf, 2); /* Length */ + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ + wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */ + tlv_len2 = wpabuf_put(buf, 2); /* Length */ + + /* SoH Header */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */ + outer_len = wpabuf_put(buf, 2); + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + wpabuf_put_be16(buf, ver); /* Inner Type */ + inner_len = wpabuf_put(buf, 2); + + if (ver == 2) { + /* SoH Mode Sub-Header */ + /* Outer Type */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); + wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */ + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + /* Value: */ + wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); + wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */ + wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */ + } + + /* SSoH TLV */ + /* System-Health-Id */ + wpabuf_put_be16(buf, 0x0002); /* Type */ + wpabuf_put_be16(buf, 4); /* Length */ + wpabuf_put_be32(buf, 79616); + /* Vendor-Specific Attribute */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); + ssoh_len = wpabuf_put(buf, 2); + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + + /* MS-Packet-Info */ + wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO); + /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be: + * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP + * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit + * would not be in the specified location. + * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits) + */ + wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */ + + /* MS-Machine-Inventory */ + /* TODO: get correct values; 0 = not applicable for OS */ + wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY); + wpabuf_put_be32(buf, 0); /* osVersionMajor */ + wpabuf_put_be32(buf, 0); /* osVersionMinor */ + wpabuf_put_be32(buf, 0); /* osVersionBuild */ + wpabuf_put_be16(buf, 0); /* spVersionMajor */ + wpabuf_put_be16(buf, 0); /* spVersionMinor */ + wpabuf_put_be16(buf, 0); /* procArch */ + + /* MS-MachineName */ + wpabuf_put_u8(buf, SSOH_MS_MACHINENAME); + wpabuf_put_be16(buf, os_strlen(machinename) + 1); + wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1); + + /* MS-CorrelationId */ + wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID); + wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); + + /* MS-Quarantine-State */ + wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE); + wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */ + wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */ + wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */ + wpabuf_put_be16(buf, 1); /* urlLenInBytes */ + wpabuf_put_u8(buf, 0); /* null termination for the url */ + + /* MS-Machine-Inventory-Ex */ + wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX); + wpabuf_put_be32(buf, 0); /* Reserved + * (note: Windows XP SP3 uses 0xdecafbad) */ + wpabuf_put_u8(buf, 1); /* ProductType: Client */ + + /* Update SSoH Length */ + end = wpabuf_put(buf, 0); + WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2); + + /* TODO: SoHReportEntry TLV (zero or more) */ + + /* Update length fields */ + end = wpabuf_put(buf, 0); + WPA_PUT_BE16(tlv_len, end - tlv_len - 2); + WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2); + WPA_PUT_BE16(outer_len, end - outer_len - 2); + WPA_PUT_BE16(inner_len, end - inner_len - 2); + + return buf; +} + + +struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len) +{ + const u8 *pos; + + wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len); + + if (len < 12) + return NULL; + + /* SoH Request */ + pos = data; + + /* TLV Type */ + if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV) + return NULL; + pos += 2; + + /* Length */ + if (WPA_GET_BE16(pos) < 8) + return NULL; + pos += 2; + + /* Vendor_Id */ + if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT) + return NULL; + pos += 4; + + /* TLV Type */ + if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */) + return NULL; + + wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received"); + + return tncc_build_soh(2); +} diff --git a/src/eap_peer/tncc.h b/src/eap_peer/tncc.h new file mode 100644 index 0000000000000..4d42a05b9a0e2 --- /dev/null +++ b/src/eap_peer/tncc.h @@ -0,0 +1,42 @@ +/* + * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TNCC_H +#define TNCC_H + +struct tncc_data; + +struct tncc_data * tncc_init(void); +void tncc_deinit(struct tncc_data *tncc); +void tncc_init_connection(struct tncc_data *tncc); +size_t tncc_total_send_len(struct tncc_data *tncc); +u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos); +char * tncc_if_tnccs_start(struct tncc_data *tncc); +char * tncc_if_tnccs_end(void); + +enum tncc_process_res { + TNCCS_PROCESS_ERROR = -1, + TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, + TNCCS_RECOMMENDATION_ERROR, + TNCCS_RECOMMENDATION_ALLOW, + TNCCS_RECOMMENDATION_NONE, + TNCCS_RECOMMENDATION_ISOLATE +}; + +enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, + const u8 *msg, size_t len); + +struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len); + +#endif /* TNCC_H */ diff --git a/src/eap_server/.gitignore b/src/eap_server/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/eap_server/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/eap_server/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/eap.c b/src/eap_server/eap.c index da250f04c783f..dea91e635d2f4 100644 --- a/contrib/hostapd/eap.c +++ b/src/eap_server/eap.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP Standalone Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,12 +10,17 @@ * license. * * See README and COPYING for more details. + * + * This state machine is based on the full authenticator state machine defined + * in RFC 4137. However, to support backend authentication in RADIUS + * authentication server functionality, parts of backend authenticator (also + * from RFC 4137) are mixed in. This functionality is enabled by setting + * backend_auth configuration variable to TRUE. */ #include "includes.h" -#include "hostapd.h" -#include "sta_info.h" +#include "common.h" #include "eap_i.h" #include "state_machine.h" @@ -32,50 +37,54 @@ static void eap_user_free(struct eap_user *user); static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, int methodTimeout); -static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len); -static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len); -static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len); +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp); +static int eap_sm_getId(const struct wpabuf *data); +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id); +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id); static int eap_sm_nextId(struct eap_sm *sm, int id); -static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len); +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len); static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor); static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); -static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) { - return sm->eapol_cb->get_bool(sm->eapol_ctx, var); -} - + if (src == NULL) + return -1; -static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, - Boolean value) -{ - sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); + wpabuf_free(*dst); + *dst = wpabuf_dup(src); + return *dst ? 0 : -1; } -static void eapol_set_eapReqData(struct eap_sm *sm, - const u8 *eapReqData, size_t eapReqDataLen) +static int eap_copy_data(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) { - wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL", - sm->eapReqData, sm->eapReqDataLen); - sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen); -} - + if (src == NULL) + return -1; -static void eapol_set_eapKeyData(struct eap_sm *sm, - const u8 *eapKeyData, size_t eapKeyDataLen) -{ - wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL", - sm->eapKeyData, sm->eapKeyDataLen); - sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen); + os_free(*dst); + *dst = os_malloc(src_len); + if (*dst) { + os_memcpy(*dst, src, src_len); + *dst_len = src_len; + return 0; + } else { + *dst_len = 0; + return -1; + } } +#define EAP_COPY(dst, src) \ + eap_copy_data((dst), (dst ## Len), (src), (src ## Len)) + /** * eap_user_get - Fetch user information from the database - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * @identity: Identity (User-Name) of the user * @identity_len: Length of identity in bytes * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user @@ -98,7 +107,7 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, eap_user_free(sm->user); sm->user = NULL; - user = wpa_zalloc(sizeof(*user)); + user = os_zalloc(sizeof(*user)); if (user == NULL) return -1; @@ -127,14 +136,14 @@ SM_STATE(EAP, INITIALIZE) SM_ENTRY(EAP, INITIALIZE); sm->currentId = -1; - eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); - eapol_set_bool(sm, EAPOL_eapFail, FALSE); - eapol_set_bool(sm, EAPOL_eapTimeout, FALSE); - free(sm->eapKeyData); - sm->eapKeyData = NULL; - sm->eapKeyDataLen = 0; - /* eapKeyAvailable = FALSE */ - eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + sm->eap_if.eapSuccess = FALSE; + sm->eap_if.eapFail = FALSE; + sm->eap_if.eapTimeout = FALSE; + os_free(sm->eap_if.eapKeyData); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + sm->eap_if.eapKeyAvailable = FALSE; + sm->eap_if.eapRestart = FALSE; /* * This is not defined in RFC 4137, but method state needs to be @@ -151,7 +160,7 @@ SM_STATE(EAP, INITIALIZE) if (sm->backend_auth) { sm->currentMethod = EAP_TYPE_NONE; /* parse rxResp, respId, respMethod */ - eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); if (sm->rxResp) { sm->currentId = sm->respId; } @@ -171,8 +180,8 @@ SM_STATE(EAP, PICK_UP_METHOD) sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } - sm->m = eap_sm_get_eap_methods(EAP_VENDOR_IETF, - sm->currentMethod); + sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF, + sm->currentMethod); if (sm->m && sm->m->initPickUp) { sm->eap_method_priv = sm->m->initPickUp(sm); if (sm->eap_method_priv == NULL) { @@ -194,9 +203,9 @@ SM_STATE(EAP, IDLE) { SM_ENTRY(EAP, IDLE); - sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount, - sm->eapSRTT, sm->eapRTTVAR, - sm->methodTimeout); + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); } @@ -204,8 +213,11 @@ SM_STATE(EAP, RETRANSMIT) { SM_ENTRY(EAP, RETRANSMIT); - /* TODO: Is this needed since EAPOL state machines take care of - * retransmit? */ + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = TRUE; + } } @@ -214,7 +226,7 @@ SM_STATE(EAP, RECEIVED) SM_ENTRY(EAP, RECEIVED); /* parse rxResp, respId, respMethod */ - eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); sm->num_rounds++; } @@ -222,8 +234,8 @@ SM_STATE(EAP, RECEIVED) SM_STATE(EAP, DISCARD) { SM_ENTRY(EAP, DISCARD); - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapNoReq = TRUE; } @@ -232,20 +244,20 @@ SM_STATE(EAP, SEND_REQUEST) SM_ENTRY(EAP, SEND_REQUEST); sm->retransCount = 0; - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->lastReqData); - sm->lastReqData = sm->eapReqData; - sm->lastReqDataLen = sm->eapReqDataLen; - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapReq, TRUE); + if (sm->eap_if.eapReqData) { + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = TRUE; + } else { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + } } else { wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapReq, FALSE); - eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + sm->eap_if.eapNoReq = TRUE; } } @@ -256,7 +268,7 @@ SM_STATE(EAP, INTEGRITY_CHECK) if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, - sm->eapRespData, sm->eapRespDataLen); + sm->eap_if.eapRespData); } } @@ -274,9 +286,9 @@ SM_STATE(EAP, METHOD_REQUEST) wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", sm->currentId); sm->lastId = sm->currentId; - free(sm->eapReqData); - sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, - sm->currentId, &sm->eapReqDataLen); + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, + sm->currentId); if (sm->m->getTimeout) sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); else @@ -288,17 +300,17 @@ SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); - sm->m->process(sm, sm->eap_method_priv, sm->eapRespData, - sm->eapRespDataLen); + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); - free(sm->eapKeyData); + os_free(sm->eap_if.eapKeyData); if (sm->m->getKey) { - sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, - &sm->eapKeyDataLen); + sm->eap_if.eapKeyData = sm->m->getKey( + sm, sm->eap_method_priv, + &sm->eap_if.eapKeyDataLen); } else { - sm->eapKeyData = NULL; - sm->eapKeyDataLen = 0; + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; } sm->methodState = METHOD_END; } else { @@ -323,7 +335,7 @@ SM_STATE(EAP, PROPOSE_METHOD) sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } - sm->m = eap_sm_get_eap_methods(vendor, type); + sm->m = eap_server_get_eap_method(vendor, type); if (sm->m) { sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { @@ -343,9 +355,10 @@ SM_STATE(EAP, PROPOSE_METHOD) SM_STATE(EAP, NAK) { - struct eap_hdr *nak; + const struct eap_hdr *nak; size_t len = 0; - u8 *pos, *nak_list = NULL; + const u8 *pos; + const u8 *nak_list = NULL; SM_ENTRY(EAP, NAK); @@ -355,12 +368,12 @@ SM_STATE(EAP, NAK) } sm->m = NULL; - nak = (struct eap_hdr *) sm->eapRespData; - if (nak && sm->eapRespDataLen > sizeof(*nak)) { - len = ntohs(nak->length); - if (len > sm->eapRespDataLen) - len = sm->eapRespDataLen; - pos = (u8 *) (nak + 1); + nak = wpabuf_head(sm->eap_if.eapRespData); + if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { + len = be_to_host16(nak->length); + if (len > wpabuf_len(sm->eap_if.eapRespData)) + len = wpabuf_len(sm->eap_if.eapRespData); + pos = (const u8 *) (nak + 1); len -= sizeof(*nak); if (*pos == EAP_TYPE_NAK) { pos++; @@ -384,7 +397,7 @@ SM_STATE(EAP, TIMEOUT_FAILURE) { SM_ENTRY(EAP, TIMEOUT_FAILURE); - eapol_set_bool(sm, EAPOL_eapTimeout, TRUE); + sm->eap_if.eapTimeout = TRUE; } @@ -392,19 +405,11 @@ SM_STATE(EAP, FAILURE) { SM_ENTRY(EAP, FAILURE); - free(sm->eapReqData); - sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId, - &sm->eapReqDataLen); - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->eapReqData); - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - } - free(sm->lastReqData); + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId); + wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; - sm->lastReqDataLen = 0; - eapol_set_bool(sm, EAPOL_eapFail, TRUE); + sm->eap_if.eapFail = TRUE; } @@ -412,31 +417,170 @@ SM_STATE(EAP, SUCCESS) { SM_ENTRY(EAP, SUCCESS); - free(sm->eapReqData); - sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId, - &sm->eapReqDataLen); - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->eapReqData); - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - } - free(sm->lastReqData); + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId); + wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; - sm->lastReqDataLen = 0; - if (sm->eapKeyData) { - eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen); + if (sm->eap_if.eapKeyData) + sm->eap_if.eapKeyAvailable = TRUE; + sm->eap_if.eapSuccess = TRUE; +} + + +SM_STATE(EAP, INITIALIZE_PASSTHROUGH) +{ + SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); + + wpabuf_free(sm->eap_if.aaaEapRespData); + sm->eap_if.aaaEapRespData = NULL; +} + + +SM_STATE(EAP, IDLE2) +{ + SM_ENTRY(EAP, IDLE2); + + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT2) +{ + SM_ENTRY(EAP, RETRANSMIT2); + + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = TRUE; + } +} + + +SM_STATE(EAP, RECEIVED2) +{ + SM_ENTRY(EAP, RECEIVED2); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, DISCARD2) +{ + SM_ENTRY(EAP, DISCARD2); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapNoReq = TRUE; +} + + +SM_STATE(EAP, SEND_REQUEST2) +{ + SM_ENTRY(EAP, SEND_REQUEST2); + + sm->retransCount = 0; + if (sm->eap_if.eapReqData) { + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = TRUE; + } else { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + } + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData"); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + sm->eap_if.eapNoReq = TRUE; + } +} + + +SM_STATE(EAP, AAA_REQUEST) +{ + SM_ENTRY(EAP, AAA_REQUEST); + + if (sm->eap_if.eapRespData == NULL) { + wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData"); + return; + } + + /* + * if (respMethod == IDENTITY) + * aaaIdentity = eapRespData + * This is already taken care of by the EAP-Identity method which + * stores the identity into sm->identity. + */ + + eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, AAA_RESPONSE) +{ + SM_ENTRY(EAP, AAA_RESPONSE); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->currentId = eap_sm_getId(sm->eap_if.eapReqData); + sm->methodTimeout = sm->eap_if.aaaMethodTimeout; +} + + +SM_STATE(EAP, AAA_IDLE) +{ + SM_ENTRY(EAP, AAA_IDLE); + + sm->eap_if.aaaFail = FALSE; + sm->eap_if.aaaSuccess = FALSE; + sm->eap_if.aaaEapReq = FALSE; + sm->eap_if.aaaEapNoReq = FALSE; + sm->eap_if.aaaEapResp = TRUE; +} + + +SM_STATE(EAP, TIMEOUT_FAILURE2) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE2); + + sm->eap_if.eapTimeout = TRUE; +} + + +SM_STATE(EAP, FAILURE2) +{ + SM_ENTRY(EAP, FAILURE2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->eap_if.eapFail = TRUE; +} + + +SM_STATE(EAP, SUCCESS2) +{ + SM_ENTRY(EAP, SUCCESS2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + + sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable; + if (sm->eap_if.aaaEapKeyAvailable) { + EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); + } else { + os_free(sm->eap_if.eapKeyData); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; } - eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + + sm->eap_if.eapSuccess = TRUE; } SM_STEP(EAP) { - if (eapol_get_bool(sm, EAPOL_eapRestart) && - eapol_get_bool(sm, EAPOL_portEnabled)) + if (sm->eap_if.eapRestart && sm->eap_if.portEnabled) SM_ENTER_GLOBAL(EAP, INITIALIZE); - else if (!eapol_get_bool(sm, EAPOL_portEnabled)) + else if (!sm->eap_if.portEnabled) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { @@ -471,13 +615,13 @@ SM_STEP(EAP) } break; case EAP_DISABLED: - if (eapol_get_bool(sm, EAPOL_portEnabled)) + if (sm->eap_if.portEnabled) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: - if (sm->retransWhile == 0) + if (sm->eap_if.retransWhile == 0) SM_ENTER(EAP, RETRANSMIT); - else if (eapol_get_bool(sm, EAPOL_eapResp)) + else if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: @@ -576,6 +720,8 @@ SM_STEP(EAP) SM_ENTER(EAP, FAILURE); else if (sm->decision == DECISION_SUCCESS) SM_ENTER(EAP, SUCCESS); + else if (sm->decision == DECISION_PASSTHROUGH) + SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); else SM_ENTER(EAP, PROPOSE_METHOD); break; @@ -585,6 +731,59 @@ SM_STEP(EAP) break; case EAP_SUCCESS: break; + + case EAP_INITIALIZE_PASSTHROUGH: + if (sm->currentId == -1) + SM_ENTER(EAP, AAA_IDLE); + else + SM_ENTER(EAP, AAA_REQUEST); + break; + case EAP_IDLE2: + if (sm->eap_if.eapResp) + SM_ENTER(EAP, RECEIVED2); + else if (sm->eap_if.retransWhile == 0) + SM_ENTER(EAP, RETRANSMIT2); + break; + case EAP_RETRANSMIT2: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + else + SM_ENTER(EAP, IDLE2); + break; + case EAP_RECEIVED2: + if (sm->rxResp && (sm->respId == sm->currentId)) + SM_ENTER(EAP, AAA_REQUEST); + else + SM_ENTER(EAP, DISCARD2); + break; + case EAP_DISCARD2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_SEND_REQUEST2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_AAA_REQUEST: + SM_ENTER(EAP, AAA_IDLE); + break; + case EAP_AAA_RESPONSE: + SM_ENTER(EAP, SEND_REQUEST2); + break; + case EAP_AAA_IDLE: + if (sm->eap_if.aaaFail) + SM_ENTER(EAP, FAILURE2); + else if (sm->eap_if.aaaSuccess) + SM_ENTER(EAP, SUCCESS2); + else if (sm->eap_if.aaaEapReq) + SM_ENTER(EAP, AAA_RESPONSE); + else if (sm->eap_if.aaaTimeout) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + break; + case EAP_TIMEOUT_FAILURE2: + break; + case EAP_FAILURE2: + break; + case EAP_SUCCESS2: + break; } } @@ -593,16 +792,57 @@ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, int methodTimeout) { - /* For now, retransmission is done in EAPOL state machines, so make - * sure EAP state machine does not end up trying to retransmit packets. + int rto, i; + + if (methodTimeout) { + /* + * EAP method (either internal or through AAA server, provided + * timeout hint. Use that as-is as a timeout for retransmitting + * the EAP request if no response is received. + */ + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from EAP method hint)", methodTimeout); + return methodTimeout; + } + + /* + * RFC 3748 recommends algorithms described in RFC 2988 for estimation + * of the retransmission timeout. This should be implemented once + * round-trip time measurements are available. For nowm a simple + * backoff mechanism is used instead if there are no EAP method + * specific hints. + * + * SRTT = smoothed round-trip time + * RTTVAR = round-trip time variation + * RTO = retransmission timeout */ - return 1; + + /* + * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for + * initial retransmission and then double the RTO to provide back off + * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3 + * modified RTOmax. + */ + rto = 3; + for (i = 0; i < retransCount; i++) { + rto *= 2; + if (rto >= 20) { + rto = 20; + break; + } + } + + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from dynamic back off; retransCount=%d)", + rto, retransCount); + + return rto; } -static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) { - struct eap_hdr *hdr; + const struct eap_hdr *hdr; size_t plen; /* parse rxResp, respId, respMethod */ @@ -612,14 +852,19 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) sm->respVendor = EAP_VENDOR_IETF; sm->respVendorMethod = EAP_TYPE_NONE; - if (resp == NULL || len < sizeof(*hdr)) + if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p " + "len=%lu", resp, + resp ? (unsigned long) wpabuf_len(resp) : 0); return; + } - hdr = (struct eap_hdr *) resp; - plen = ntohs(hdr->length); - if (plen > len) { + hdr = wpabuf_head(resp); + plen = be_to_host16(hdr->length); + if (plen > wpabuf_len(resp)) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " - "(len=%lu plen=%lu)", (unsigned long) len, + "(len=%lu plen=%lu)", + (unsigned long) wpabuf_len(resp), (unsigned long) plen); return; } @@ -652,37 +897,52 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) } -static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len) +static int eap_sm_getId(const struct wpabuf *data) +{ + const struct eap_hdr *hdr; + + if (data == NULL || wpabuf_len(data) < sizeof(*hdr)) + return -1; + + hdr = wpabuf_head(data); + wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier); + return hdr->identifier; +} + + +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id) { + struct wpabuf *msg; struct eap_hdr *resp; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); - *len = sizeof(*resp); - resp = malloc(*len); - if (resp == NULL) + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); resp->code = EAP_CODE_SUCCESS; resp->identifier = id; - resp->length = htons(*len); + resp->length = host_to_be16(sizeof(*resp)); - return (u8 *) resp; + return msg; } -static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len) +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id) { + struct wpabuf *msg; struct eap_hdr *resp; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); - *len = sizeof(*resp); - resp = malloc(*len); - if (resp == NULL) + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); resp->code = EAP_CODE_FAILURE; resp->identifier = id; - resp->length = htons(*len); + resp->length = host_to_be16(sizeof(*resp)); - return (u8 *) resp; + return msg; } @@ -701,14 +961,14 @@ static int eap_sm_nextId(struct eap_sm *sm, int id) /** * eap_sm_process_nak - Process EAP-Response/Nak - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * @nak_list: Nak list (allowed methods) from the supplicant * @len: Length of nak_list in bytes * * This function is called when EAP-Response/Nak is received from the * supplicant. This can happen for both phase 1 and phase 2 authentications. */ -void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) { int i; size_t j; @@ -745,9 +1005,9 @@ void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) not_found: /* not found - remove from the list */ - memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - (EAP_MAX_METHODS - i - 1) * - sizeof(sm->user->methods[0])); + os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1], + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); sm->user->methods[EAP_MAX_METHODS - 1].vendor = EAP_VENDOR_IETF; sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; @@ -759,7 +1019,8 @@ void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) } -static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len) +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len) { if (nak_list == NULL || sm == NULL || sm->user == NULL) return; @@ -809,6 +1070,11 @@ static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) static int eap_sm_Policy_getDecision(struct eap_sm *sm) { + if (!sm->eap_server && sm->identity) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); + return DECISION_PASSTHROUGH; + } + if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && sm->m->isSuccess(sm, sm->eap_method_priv)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " @@ -826,11 +1092,30 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) } if ((sm->user == NULL || sm->update_user) && sm->identity) { + /* + * Allow Identity method to be started once to allow identity + * selection hint to be sent from the authentication server, + * but prevent a loop of Identity requests by only allowing + * this to happen once. + */ + int id_req = 0; + if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) + id_req = 1; if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " "found from database -> FAILURE"); return DECISION_FAILURE; } + if (id_req && sm->user && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: stop " + "identity request loop -> FAILURE"); + sm->update_user = TRUE; + return DECISION_FAILURE; + } sm->update_user = FALSE; } @@ -863,15 +1148,15 @@ static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) /** - * eap_sm_step - Step EAP state machine - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * eap_server_sm_step - Step EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: 1 if EAP state was changed or 0 if not * * This function advances EAP state machine to a new state to match with the * current variables. This should be called whenever variables used by the EAP * state machine have changed. */ -int eap_sm_step(struct eap_sm *sm) +int eap_server_sm_step(struct eap_sm *sm) { int res = 0; do { @@ -884,42 +1169,18 @@ int eap_sm_step(struct eap_sm *sm) } -/** - * eap_set_eapRespData - Set EAP response (eapRespData) - * @sm: Pointer to EAP state machine allocated with eap_sm_init() - * @eapRespData: EAP-Response payload from the supplicant - * @eapRespDataLen: Length of eapRespData in bytes - * - * This function is called when an EAP-Response is received from a supplicant. - */ -void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, - size_t eapRespDataLen) -{ - if (sm == NULL) - return; - free(sm->eapRespData); - sm->eapRespData = malloc(eapRespDataLen); - if (sm->eapRespData == NULL) - return; - memcpy(sm->eapRespData, eapRespData, eapRespDataLen); - sm->eapRespDataLen = eapRespDataLen; - wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received", - eapRespData, eapRespDataLen); -} - - static void eap_user_free(struct eap_user *user) { if (user == NULL) return; - free(user->password); + os_free(user->password); user->password = NULL; - free(user); + os_free(user); } /** - * eap_sm_init - Allocate and initialize EAP state machine + * eap_server_sm_init - Allocate and initialize EAP server state machine * @eapol_ctx: Context data to be used with eapol_cb calls * @eapol_cb: Pointer to EAPOL callback functions * @conf: EAP configuration @@ -927,54 +1188,88 @@ static void eap_user_free(struct eap_user *user) * * This function allocates and initializes an EAP state machine. */ -struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *conf) +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + struct eap_config *conf) { struct eap_sm *sm; - sm = wpa_zalloc(sizeof(*sm)); + sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; - sm->MaxRetrans = 10; + sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ sm->ssl_ctx = conf->ssl_ctx; sm->eap_sim_db_priv = conf->eap_sim_db_priv; sm->backend_auth = conf->backend_auth; - - wpa_printf(MSG_DEBUG, "EAP: State machine created"); + sm->eap_server = conf->eap_server; + if (conf->pac_opaque_encr_key) { + sm->pac_opaque_encr_key = os_malloc(16); + if (sm->pac_opaque_encr_key) { + os_memcpy(sm->pac_opaque_encr_key, + conf->pac_opaque_encr_key, 16); + } + } + if (conf->eap_fast_a_id) { + sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); + if (sm->eap_fast_a_id) { + os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id, + conf->eap_fast_a_id_len); + sm->eap_fast_a_id_len = conf->eap_fast_a_id_len; + } + } + if (conf->eap_fast_a_id_info) + sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); + sm->eap_fast_prov = conf->eap_fast_prov; + sm->pac_key_lifetime = conf->pac_key_lifetime; + sm->pac_key_refresh_time = conf->pac_key_refresh_time; + sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + sm->tnc = conf->tnc; + sm->wps = conf->wps; + if (conf->assoc_wps_ie) + sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + + wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); return sm; } /** - * eap_sm_deinit - Deinitialize and free an EAP state machine - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * eap_server_sm_deinit - Deinitialize and free an EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function deinitializes EAP state machine and frees all allocated * resources. */ -void eap_sm_deinit(struct eap_sm *sm) +void eap_server_sm_deinit(struct eap_sm *sm) { if (sm == NULL) return; - wpa_printf(MSG_DEBUG, "EAP: State machine removed"); + wpa_printf(MSG_DEBUG, "EAP: Server state machine removed"); if (sm->m && sm->eap_method_priv) sm->m->reset(sm, sm->eap_method_priv); - free(sm->eapReqData); - free(sm->eapKeyData); - free(sm->lastReqData); - free(sm->eapRespData); - free(sm->identity); + wpabuf_free(sm->eap_if.eapReqData); + os_free(sm->eap_if.eapKeyData); + os_free(sm->lastReqData); + wpabuf_free(sm->eap_if.eapRespData); + os_free(sm->identity); + os_free(sm->pac_opaque_encr_key); + os_free(sm->eap_fast_a_id); + os_free(sm->eap_fast_a_id_info); + wpabuf_free(sm->eap_if.aaaEapReqData); + wpabuf_free(sm->eap_if.aaaEapRespData); + os_free(sm->eap_if.aaaEapKeyData); eap_user_free(sm->user); - free(sm); + wpabuf_free(sm->assoc_wps_ie); + os_free(sm); } /** * eap_sm_notify_cached - Notify EAP state machine of cached PMK - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function is called when PMKSA caching is used to skip EAP * authentication. @@ -990,7 +1285,7 @@ void eap_sm_notify_cached(struct eap_sm *sm) /** * eap_sm_pending_cb - EAP state machine callback for a pending EAP request - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * * This function is called when data for a pending EAP-Request is received. */ @@ -1006,7 +1301,7 @@ void eap_sm_pending_cb(struct eap_sm *sm) /** * eap_sm_method_pending - Query whether EAP method is waiting for pending data - * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: 1 if method is waiting for pending data or 0 if not */ int eap_sm_method_pending(struct eap_sm *sm) @@ -1018,121 +1313,24 @@ int eap_sm_method_pending(struct eap_sm *sm) /** - * eap_hdr_validate - Validate EAP header - * @vendor: Expected EAP Vendor-Id (0 = IETF) - * @eap_type: Expected EAP type number - * @msg: EAP frame (starting with EAP header) - * @msglen: Length of msg - * @plen: Pointer to variable to contain the returned payload length - * Returns: Pointer to EAP payload (after type field), or %NULL on failure - * - * This is a helper function for EAP method implementations. This is usually - * called in the beginning of struct eap_method::process() function to verify - * that the received EAP request packet has a valid header. This function is - * able to process both legacy and expanded EAP headers and in most cases, the - * caller can just use the returned payload pointer (into *plen) for processing - * the payload regardless of whether the packet used the expanded EAP header or - * not. + * eap_get_identity - Get the user identity (from EAP-Response/Identity) + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @len: Buffer for returning identity length + * Returns: Pointer to the user identity or %NULL if not available */ -const u8 * eap_hdr_validate(int vendor, EapType eap_type, - const u8 *msg, size_t msglen, size_t *plen) +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) { - const struct eap_hdr *hdr; - const u8 *pos; - size_t len; - - hdr = (const struct eap_hdr *) msg; - - if (msglen < sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); - 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; - } - - pos = (const u8 *) (hdr + 1); - - if (*pos == EAP_TYPE_EXPANDED) { - int exp_vendor; - u32 exp_type; - if (len < sizeof(*hdr) + 8) { - wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " - "length"); - return NULL; - } - pos++; - exp_vendor = WPA_GET_BE24(pos); - pos += 3; - exp_type = WPA_GET_BE32(pos); - pos += 4; - if (exp_vendor != vendor || exp_type != (u32) eap_type) { - wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " - "type"); - return NULL; - } - - *plen = len - sizeof(*hdr) - 8; - return pos; - } else { - if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { - wpa_printf(MSG_INFO, "EAP: Invalid frame type"); - return NULL; - } - *plen = len - sizeof(*hdr) - 1; - return pos + 1; - } + *len = sm->identity_len; + return sm->identity; } /** - * eap_msg_alloc - Allocate a buffer for an EAP message - * @vendor: Vendor-Id (0 = IETF) - * @type: EAP type - * @len: Buffer for returning message length - * @payload_len: Payload length in bytes (data after Type) - * @code: Message Code (EAP_CODE_*) - * @identifier: Identifier - * @payload: Pointer to payload pointer that will be set to point to the - * beginning of the payload or %NULL if payload pointer is not needed - * Returns: Pointer to the allocated message buffer or %NULL on error - * - * This function can be used to allocate a buffer for an EAP message and fill - * in the EAP header. This function is automatically using expanded EAP header - * if the selected Vendor-Id is not IETF. In other words, most EAP methods do - * not need to separately select which header type to use when using this - * function to allocate the message buffers. + * eap_get_interface - Get pointer to EAP-EAPOL interface data + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the EAP-EAPOL interface data */ -struct eap_hdr * eap_msg_alloc(int vendor, EapType type, size_t *len, - size_t payload_len, u8 code, u8 identifier, - u8 **payload) +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) { - struct eap_hdr *hdr; - u8 *pos; - - *len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + - payload_len; - hdr = malloc(*len); - if (hdr) { - hdr->code = code; - hdr->identifier = identifier; - hdr->length = host_to_be16(*len); - pos = (u8 *) (hdr + 1); - if (vendor == EAP_VENDOR_IETF) { - *pos++ = type; - } else { - *pos++ = EAP_TYPE_EXPANDED; - WPA_PUT_BE24(pos, vendor); - pos += 3; - WPA_PUT_BE32(pos, type); - pos += 4; - } - if (payload) - *payload = pos; - } - - return hdr; + return &sm->eap_if; } diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h new file mode 100644 index 0000000000000..6a20da4f46b0b --- /dev/null +++ b/src/eap_server/eap.h @@ -0,0 +1,122 @@ +/* + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "defs.h" +#include "eap_common/eap_defs.h" +#include "eap_server/eap_methods.h" +#include "wpabuf.h" + +struct eap_sm; + +#define EAP_MAX_METHODS 8 + +#define EAP_TTLS_AUTH_PAP 1 +#define EAP_TTLS_AUTH_CHAP 2 +#define EAP_TTLS_AUTH_MSCHAP 4 +#define EAP_TTLS_AUTH_MSCHAPV2 8 + +struct eap_user { + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int password_hash; /* whether password is hashed with + * nt_password_hash() */ + int phase2; + int force_version; + int ttls_auth; /* bitfield of + * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ +}; + +struct eap_eapol_interface { + /* Lower layer to full authenticator variables */ + Boolean eapResp; /* shared with EAPOL Backend Authentication */ + struct wpabuf *eapRespData; + Boolean portEnabled; + int retransWhile; + Boolean eapRestart; /* shared with EAPOL Authenticator PAE */ + int eapSRTT; + int eapRTTVAR; + + /* Full authenticator to lower layer variables */ + Boolean eapReq; /* shared with EAPOL Backend Authentication */ + Boolean eapNoReq; /* shared with EAPOL Backend Authentication */ + Boolean eapSuccess; + Boolean eapFail; + Boolean eapTimeout; + struct wpabuf *eapReqData; + u8 *eapKeyData; + size_t eapKeyDataLen; + Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ + + /* AAA interface to full authenticator variables */ + Boolean aaaEapReq; + Boolean aaaEapNoReq; + Boolean aaaSuccess; + Boolean aaaFail; + struct wpabuf *aaaEapReqData; + u8 *aaaEapKeyData; + size_t aaaEapKeyDataLen; + Boolean aaaEapKeyAvailable; + int aaaMethodTimeout; + + /* Full authenticator to AAA interface variables */ + Boolean aaaEapResp; + struct wpabuf *aaaEapRespData; + /* aaaIdentity -> eap_get_identity() */ + Boolean aaaTimeout; +}; + +struct eapol_callbacks { + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + const char * (*get_eap_req_id_text)(void *ctx, size_t *len); +}; + +struct eap_config { + void *ssl_ctx; + void *eap_sim_db_priv; + Boolean backend_auth; + int eap_server; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + const struct wpabuf *assoc_wps_ie; +}; + + +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + struct eap_config *eap_conf); +void eap_server_sm_deinit(struct eap_sm *sm); +int eap_server_sm_step(struct eap_sm *sm); +void eap_sm_notify_cached(struct eap_sm *sm); +void eap_sm_pending_cb(struct eap_sm *sm); +int eap_sm_method_pending(struct eap_sm *sm); +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); + +#endif /* EAP_H */ diff --git a/contrib/hostapd/eap_aka.c b/src/eap_server/eap_aka.c index 9b10c8e1eb018..aad52fd649a5a 100644 --- a/contrib/hostapd/eap_aka.c +++ b/src/eap_server/eap_aka.c @@ -1,5 +1,5 @@ /* - * hostapd / EAP-AKA (RFC 4187) + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -14,19 +14,21 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_sim_common.h" +#include "eap_server/eap_sim_db.h" +#include "sha1.h" +#include "sha256.h" #include "crypto.h" -#include "eap_i.h" -#include "eap_sim_common.h" -#include "eap_sim_db.h" struct eap_aka_data { u8 mk[EAP_SIM_MK_LEN]; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; - u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN]; @@ -45,6 +47,14 @@ struct eap_aka_data { int auts_reported; /* whether the current AUTS has been reported to the * eap_sim_db */ u16 notification; + int use_result_ind; + + struct wpabuf *id_msgs; + int pending_id; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; }; @@ -92,33 +102,174 @@ static void * eap_aka_init(struct eap_sm *sm) return NULL; } - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; + + data->eap_method = EAP_TYPE_AKA; + + data->state = IDENTITY; + eap_aka_determine_identity(sm, data, 1, 0); + data->pending_id = -1; + + return data; +} + + +#ifdef EAP_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + /* TODO: make ANID configurable; see 3GPP TS 24.302 */ + char *network_name = "WLAN"; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA_PRIME; + data->network_name = os_malloc(os_strlen(network_name)); + if (data->network_name == NULL) { + os_free(data); + return NULL; + } + + data->network_name_len = os_strlen(network_name); + os_memcpy(data->network_name, network_name, data->network_name_len); + data->state = IDENTITY; eap_aka_determine_identity(sm, data, 1, 0); + data->pending_id = -1; return data; } +#endif /* EAP_AKA_PRIME */ static void eap_aka_reset(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; - free(data->next_pseudonym); - free(data->next_reauth_id); - free(data); + os_free(data->next_pseudonym); + os_free(data->next_reauth_id); + wpabuf_free(data->id_msgs); + os_free(data->network_name); + os_free(data); } -static u8 * eap_aka_build_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int id, size_t *reqDataLen) +static int eap_aka_add_id_msg(struct eap_aka_data *data, + const struct wpabuf *msg) +{ + if (msg == NULL) + return -1; + + if (data->id_msgs == NULL) { + data->id_msgs = wpabuf_dup(msg); + return data->id_msgs == NULL ? -1 : 0; + } + + if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) + return -1; + wpabuf_put_buf(data->id_msgs, msg); + + return 0; +} + + +static void eap_aka_add_checkcode(struct eap_aka_data *data, + struct eap_sim_msg *msg) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + + wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); + + if (data->id_msgs == NULL) { + /* + * No EAP-AKA/Identity packets were exchanged - send empty + * checkcode. + */ + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); + return; + } + + /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); + + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); +} + + +static int eap_aka_verify_checkcode(struct eap_aka_data *data, + const u8 *checkcode, size_t checkcode_len) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + + if (checkcode == NULL) + return -1; + + if (data->id_msgs == NULL) { + if (checkcode_len != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " + "indicates that AKA/Identity messages were " + "used, but they were not"); + return -1; + } + return 0; + } + + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " + "that AKA/Identity message were not used, but they " + "were"); + return -1; + } + + /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); + + if (os_memcmp(hash, checkcode, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, + struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; + struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, sm->identity_len)) { @@ -133,7 +284,13 @@ static u8 * eap_aka_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); } - return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); + buf = eap_sim_msg_finish(msg, NULL, NULL, 0); + if (eap_aka_add_id_msg(data, buf) < 0) { + wpabuf_free(buf); + return NULL; + } + data->pending_id = id; + return buf; } @@ -141,10 +298,10 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_msg *msg, u16 counter, const u8 *nonce_s) { - free(data->next_pseudonym); + os_free(data->next_pseudonym); data->next_pseudonym = eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); - free(data->next_reauth_id); + os_free(data->next_reauth_id); if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); @@ -177,18 +334,18 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", data->next_pseudonym); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, - strlen(data->next_pseudonym), + os_strlen(data->next_pseudonym), (u8 *) data->next_pseudonym, - strlen(data->next_pseudonym)); + os_strlen(data->next_pseudonym)); } if (data->next_reauth_id) { wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", data->next_reauth_id); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, - strlen(data->next_reauth_id), + os_strlen(data->next_reauth_id), (u8 *) data->next_reauth_id, - strlen(data->next_reauth_id)); + os_strlen(data->next_reauth_id)); } if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { @@ -201,50 +358,109 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, } -static u8 * eap_aka_build_challenge(struct eap_sm *sm, - struct eap_aka_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RAND"); eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); + wpa_printf(MSG_DEBUG, " AT_AUTN"); eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (data->kdf) { + /* Add the selected KDF into the beginning */ + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, + NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, + NULL, 0); + wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, + data->network_name_len, + data->network_name, data->network_name_len); + } if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { eap_sim_msg_free(msg); return NULL; } + eap_aka_add_checkcode(data, msg); + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA) { + u16 flags = 0; + int i; + int aka_prime_preferred = 0; + + i = 0; + while (sm->user && i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { + if (sm->user->methods[i].method == + EAP_TYPE_AKA) + break; + if (sm->user->methods[i].method == + EAP_TYPE_AKA_PRIME) { + aka_prime_preferred = 1; + break; + } + } + i++; + } + + if (aka_prime_preferred) + flags |= EAP_AKA_BIDDING_FLAG_D; + eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); + } +#endif /* EAP_AKA_PRIME */ + wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } -static u8 * eap_aka_build_reauth(struct eap_sm *sm, - struct eap_aka_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, + struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); - if (hostapd_get_rand(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, - data->emsk); - eap_sim_derive_keys_reauth(data->counter, sm->identity, - sm->identity_len, data->nonce_s, data->mk, - data->msk, data->emsk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + sm->identity, + sm->identity_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + eap_sim_derive_keys_reauth(data->counter, sm->identity, + sm->identity_len, data->nonce_s, + data->mk, data->msk, data->emsk); + } - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_REAUTHENTICATION); if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { @@ -252,43 +468,72 @@ static u8 * eap_aka_build_reauth(struct eap_sm *sm, return NULL; } + eap_aka_add_checkcode(data, msg); + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } -static u8 * eap_aka_build_notification(struct eap_sm *sm, - struct eap_aka_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_NOTIFICATION); - wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, NULL, 0); - return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); + if (data->use_result_ind) { + if (data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", + data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " + "encrypt AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } -static u8 * eap_aka_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_aka_data *data = priv; data->auts_reported = 0; switch (data->state) { case IDENTITY: - return eap_aka_build_identity(sm, data, id, reqDataLen); + return eap_aka_build_identity(sm, data, id); case CHALLENGE: - return eap_aka_build_challenge(sm, data, id, reqDataLen); + return eap_aka_build_challenge(sm, data, id); case REAUTH: - return eap_aka_build_reauth(sm, data, id, reqDataLen); + return eap_aka_build_reauth(sm, data, id); case NOTIFICATION: - return eap_aka_build_notification(sm, data, id, reqDataLen); + return eap_aka_build_notification(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " "buildReq", data->state); @@ -299,15 +544,15 @@ static u8 * eap_aka_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_aka_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_hdr *resp; - u8 *pos; + struct eap_aka_data *data = priv; + const u8 *pos; + size_t len; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_AKA || - (ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); + if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); return TRUE; } @@ -389,14 +634,33 @@ static void eap_aka_determine_identity(struct eap_sm *sm, data->reauth = eap_sim_db_get_reauth_entry( sm->eap_sim_db_priv, sm->identity, sm->identity_len); + if (data->reauth && + data->reauth->aka_prime != + (data->eap_method == EAP_TYPE_AKA_PRIME)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " + "was for different AKA version"); + data->reauth = NULL; + } if (data->reauth) { wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " "re-authentication"); identity = data->reauth->identity; identity_len = data->reauth->identity_len; data->counter = data->reauth->counter; - memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + os_memcpy(data->k_encr, + data->reauth->k_encr, + EAP_SIM_K_ENCR_LEN); + os_memcpy(data->k_aut, + data->reauth->k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(data->k_re, + data->reauth->k_re, + EAP_AKA_PRIME_K_RE_LEN); + } else { + os_memcpy(data->mk, data->reauth->mk, + EAP_SIM_MK_LEN); + } } } } @@ -437,6 +701,17 @@ static void eap_aka_determine_identity(struct eap_sm *sm, return; } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^AK for CK',IK' derivation */ + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_AKA_PRIME */ + data->reauth = NULL; data->counter = 0; /* reset re-auth counter since this is full auth */ @@ -462,10 +737,16 @@ static void eap_aka_determine_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", sm->identity, identity_len); - eap_aka_derive_mk(sm->identity, identity_len, data->ik, data->ck, - data->mk); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, - data->emsk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(sm->identity, identity_len, data->ik, + data->ck, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } eap_aka_state(data, CHALLENGE); } @@ -473,7 +754,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, static void eap_aka_process_identity(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); @@ -487,22 +768,38 @@ static void eap_aka_process_identity(struct eap_sm *sm, } if (attr->identity) { - free(sm->identity); - sm->identity = malloc(attr->identity_len); + os_free(sm->identity); + sm->identity = os_malloc(attr->identity_len); if (sm->identity) { - memcpy(sm->identity, attr->identity, - attr->identity_len); + os_memcpy(sm->identity, attr->identity, + attr->identity_len); sm->identity_len = attr->identity_len; } } eap_aka_determine_identity(sm, data, 0, 0); + if (eap_get_id(respData) == data->pending_id) { + data->pending_id = -1; + eap_aka_add_id_msg(data, respData); + } +} + + +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); } static void eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { const u8 *identity; @@ -510,9 +807,42 @@ static void eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); +#ifdef EAP_AKA_PRIME +#if 0 + /* KDF negotiation; to be enabled only after more than one KDF is + * supported */ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + attr->kdf_count == 1 && attr->mac == NULL) { + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { + wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " + "unknown KDF"); + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + data->kdf = attr->kdf[0]; + + /* Allow negotiation to continue with the selected KDF by + * sending another Challenge message */ + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + return; + } +#endif +#endif /* EAP_AKA_PRIME */ + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, - NULL, 0)) { + eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "did not include valid AT_MAC"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; @@ -520,10 +850,19 @@ static void eap_aka_process_challenge(struct eap_sm *sm, return; } - if (attr->res == NULL || attr->res_len != data->res_len || - memcmp(attr->res, data->res, data->res_len) != 0) { + /* + * AT_RES is padded, so verify that there is enough room for RES and + * that the RES length in bits matches with the expected RES. + */ + if (attr->res == NULL || attr->res_len < data->res_len || + attr->res_len_bits != data->res_len * 8 || + os_memcmp(attr->res, data->res, data->res_len) != 0) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " - "include valid AT_RES"); + "include valid AT_RES (attr len=%lu, res len=%lu " + "bits, expected %lu bits)", + (unsigned long) attr->res_len, + (unsigned long) attr->res_len_bits, + (unsigned long) data->res_len * 8); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); return; @@ -531,7 +870,12 @@ static void eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " "correct AT_MAC"); - eap_aka_state(data, SUCCESS); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_aka_state(data, NOTIFICATION); + } else + eap_aka_state(data, SUCCESS); identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, sm->identity_len, &identity_len); @@ -547,10 +891,23 @@ static void eap_aka_process_challenge(struct eap_sm *sm, data->next_pseudonym = NULL; } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, - data->next_reauth_id, data->counter + 1, - data->mk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { +#ifdef EAP_AKA_PRIME + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); +#endif /* EAP_AKA_PRIME */ + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->mk); + } data->next_reauth_id = NULL; } } @@ -558,7 +915,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, static void eap_aka_process_sync_failure(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); @@ -592,7 +949,7 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, static void eap_aka_process_reauth(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; @@ -603,8 +960,8 @@ static void eap_aka_process_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, - data->nonce_s, EAP_SIM_NONCE_S_LEN)) { + eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, + EAP_SIM_NONCE_S_LEN)) { wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " "did not include valid AT_MAC"); goto fail; @@ -631,7 +988,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, eattr.counter, data->counter); goto fail; } - free(decrypted); + os_free(decrypted); decrypted = NULL; wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " @@ -645,7 +1002,12 @@ static void eap_aka_process_reauth(struct eap_sm *sm, return; } - eap_aka_state(data, SUCCESS); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_aka_state(data, NOTIFICATION); + } else + eap_aka_state(data, SUCCESS); if (data->reauth) { identity = data->reauth->identity; @@ -668,9 +1030,23 @@ static void eap_aka_process_reauth(struct eap_sm *sm, data->next_pseudonym = NULL; } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, data->next_reauth_id, - data->counter + 1, data->mk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { +#ifdef EAP_AKA_PRIME + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); +#endif /* EAP_AKA_PRIME */ + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->mk); + } data->next_reauth_id = NULL; } else { eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); @@ -684,24 +1060,27 @@ fail: eap_aka_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; - free(decrypted); + os_free(decrypted); } static void eap_aka_process_client_error(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", attr->client_error_code); - eap_aka_state(data, FAILURE); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_aka_state(data, SUCCESS); + else + eap_aka_state(data, FAILURE); } static void eap_aka_process_authentication_reject( struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, struct eap_sim_attrs *attr) + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); eap_aka_state(data, FAILURE); @@ -710,26 +1089,34 @@ static void eap_aka_process_authentication_reject( static void eap_aka_process_notification(struct eap_sm *sm, struct eap_aka_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); - eap_aka_state(data, FAILURE); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_aka_state(data, SUCCESS); + else + eap_aka_state(data, FAILURE); } static void eap_aka_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_aka_data *data = priv; - struct eap_hdr *resp; - u8 *pos, subtype; + const u8 *pos, *end; + u8 subtype; size_t len; struct eap_sim_attrs attr; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - subtype = pos[1]; + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); + if (pos == NULL || len < 3) + return; + + end = pos + len; + subtype = *pos; + pos += 3; if (eap_aka_subtype_ok(data, subtype)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " @@ -739,10 +1126,9 @@ static void eap_aka_process(struct eap_sm *sm, void *priv, return; } - len = be_to_host16(resp->length); - pos += 4; - - if (eap_sim_parse_attr(pos, respData + len, &attr, 1, 0)) { + if (eap_sim_parse_attr(pos, end, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, + 0)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); @@ -750,34 +1136,33 @@ static void eap_aka_process(struct eap_sm *sm, void *priv, } if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { - eap_aka_process_client_error(sm, data, respData, len, &attr); + eap_aka_process_client_error(sm, data, respData, &attr); return; } if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { - eap_aka_process_authentication_reject(sm, data, respData, len, + eap_aka_process_authentication_reject(sm, data, respData, &attr); return; } switch (data->state) { case IDENTITY: - eap_aka_process_identity(sm, data, respData, len, &attr); + eap_aka_process_identity(sm, data, respData, &attr); break; case CHALLENGE: if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { - eap_aka_process_sync_failure(sm, data, respData, len, + eap_aka_process_sync_failure(sm, data, respData, &attr); } else { - eap_aka_process_challenge(sm, data, respData, len, - &attr); + eap_aka_process_challenge(sm, data, respData, &attr); } break; case REAUTH: - eap_aka_process_reauth(sm, data, respData, len, &attr); + eap_aka_process_reauth(sm, data, respData, &attr); break; case NOTIFICATION: - eap_aka_process_notification(sm, data, respData, len, &attr); + eap_aka_process_notification(sm, data, respData, &attr); break; default: wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " @@ -802,10 +1187,10 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -819,10 +1204,10 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_EMSK_LEN); + key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->emsk, EAP_EMSK_LEN); + os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } @@ -860,3 +1245,34 @@ int eap_server_aka_register(void) eap_server_method_free(eap); return ret; } + + +#ifdef EAP_AKA_PRIME +int eap_server_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->reset = eap_aka_reset; + eap->buildReq = eap_aka_buildReq; + eap->check = eap_aka_check; + eap->process = eap_aka_process; + eap->isDone = eap_aka_isDone; + eap->getKey = eap_aka_getKey; + eap->isSuccess = eap_aka_isSuccess; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + + return ret; +} +#endif /* EAP_AKA_PRIME */ diff --git a/src/eap_server/eap_fast.c b/src/eap_server/eap_fast.c new file mode 100644 index 0000000000000..b474c99a1ff3d --- /dev/null +++ b/src/eap_server/eap_fast.c @@ -0,0 +1,1596 @@ +/* + * EAP-FAST server (RFC 4851) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes_wrap.h" +#include "sha1.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "tls.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_fast_common.h" + + +static void eap_fast_reset(struct eap_sm *sm, void *priv); + + +/* Private PAC-Opaque TLV types */ +#define PAC_OPAQUE_TYPE_PAD 0 +#define PAC_OPAQUE_TYPE_KEY 1 +#define PAC_OPAQUE_TYPE_LIFETIME 2 +#define PAC_OPAQUE_TYPE_IDENTITY 3 + +struct eap_fast_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, + CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE + } state; + + int fast_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int force_version; + int peer_version; + + u8 crypto_binding_nonce[32]; + int final_result; + + struct eap_fast_key_block_provisioning *key_block_p; + + u8 simck[EAP_FAST_SIMCK_LEN]; + u8 cmk[EAP_FAST_CMK_LEN]; + int simck_idx; + + u8 pac_opaque_encr[16]; + u8 *srv_id; + size_t srv_id_len; + char *srv_id_info; + + int anon_provisioning; + int send_new_pac; /* server triggered re-keying of Tunnel PAC */ + struct wpabuf *pending_phase2_resp; + u8 *identity; /* from PAC-Opaque */ + size_t identity_len; + int eap_seq; + int tnc_started; + + int pac_key_lifetime; + int pac_key_refresh_time; +}; + + +static const char * eap_fast_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case CRYPTO_BINDING: + return "CRYPTO_BINDING"; + case REQUEST_PAC: + return "REQUEST_PAC"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_fast_state(struct eap_fast_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s", + eap_fast_state_txt(data->state), + eap_fast_state_txt(state)); + data->state = state; +} + + +static EapType eap_fast_req_failure(struct eap_sm *sm, + struct eap_fast_data *data) +{ + /* TODO: send Result TLV(FAILURE) */ + eap_fast_state(data, FAILURE); + return EAP_TYPE_NONE; +} + + +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_fast_data *data = ctx; + const u8 *pac_opaque; + size_t pac_opaque_len; + u8 *buf, *pos, *end, *pac_key = NULL; + os_time_t lifetime = 0; + struct os_time now; + u8 *identity = NULL; + size_t identity_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)", + ticket, len); + + if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid " + "SessionTicket"); + return 0; + } + + pac_opaque_len = WPA_GET_BE16(ticket + 2); + pac_opaque = ticket + 4; + if (pac_opaque_len < 8 || pac_opaque_len % 8 || + pac_opaque_len > len - 4) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque " + "(len=%lu left=%lu)", + (unsigned long) pac_opaque_len, + (unsigned long) len); + return 0; + } + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque", + pac_opaque, pac_opaque_len); + + buf = os_malloc(pac_opaque_len - 8); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " + "for decrypting PAC-Opaque"); + return 0; + } + + if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8, + pac_opaque, buf) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt " + "PAC-Opaque"); + os_free(buf); + /* + * This may have been caused by server changing the PAC-Opaque + * encryption key, so just ignore this PAC-Opaque instead of + * failing the authentication completely. Provisioning can now + * be used to provision a new PAC. + */ + return 0; + } + + end = buf + pac_opaque_len - 8; + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque", + buf, end - buf); + + pos = buf; + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case PAC_OPAQUE_TYPE_PAD: + pos = end; + break; + case PAC_OPAQUE_TYPE_KEY: + if (pos[1] != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key length %d", pos[1]); + os_free(buf); + return -1; + } + pac_key = pos + 2; + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from " + "decrypted PAC-Opaque", + pac_key, EAP_FAST_PAC_KEY_LEN); + break; + case PAC_OPAQUE_TYPE_LIFETIME: + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key lifetime length %d", + pos[1]); + os_free(buf); + return -1; + } + lifetime = WPA_GET_BE32(pos + 2); + break; + case PAC_OPAQUE_TYPE_IDENTITY: + identity = pos + 2; + identity_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + if (pac_key == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in " + "PAC-Opaque"); + os_free(buf); + return -1; + } + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from " + "PAC-Opaque", identity, identity_len); + os_free(data->identity); + data->identity = os_malloc(identity_len); + if (data->identity) { + os_memcpy(data->identity, identity, identity_len); + data->identity_len = identity_len; + } + } + + if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore " + "(lifetime=%ld now=%ld)", lifetime, now.sec); + data->send_new_pac = 2; + /* + * Allow PAC to be used to allow a PAC update with some level + * of server authentication (i.e., do not fall back to full TLS + * handshake since we cannot be sure that the peer would be + * able to validate server certificate now). However, reject + * the authentication since the PAC was not valid anymore. Peer + * can connect again with the newly provisioned PAC after this. + */ + } else if (lifetime - now.sec < data->pac_key_refresh_time) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send " + "an update if authentication succeeds"); + data->send_new_pac = 1; + } + + eap_fast_derive_master_secret(pac_key, server_random, client_random, + master_secret); + + os_free(buf); + + return 1; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 *sks; + + /* RFC 4851, Section 5.1: + * Extra key material after TLS key_block: session_key_seed[40] + */ + + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + EAP_FAST_SKS_LEN); + if (sks == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " + "session_key_seed"); + return; + } + + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + sks, EAP_FAST_SKS_LEN); + data->simck_idx = 0; + os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); + os_free(sks); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + os_free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, + "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + data->simck_idx = 0; + os_memcpy(data->simck, data->key_block_p->session_key_seed, + EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static int eap_fast_get_phase2_key(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return -1; + } + + if (data->phase2_method->getKey == NULL) + return 0; + + if ((key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + if (key_len == 32 && + data->phase2_method->vendor == EAP_VENDOR_IETF && + data->phase2_method->method == EAP_TYPE_MSCHAPV2) { + /* + * EAP-FAST uses reverse order for MS-MPPE keys when deriving + * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct + * ISK for EAP-FAST cryptobinding. + */ + os_memcpy(isk, key + 16, 16); + os_memcpy(isk + 16, key, 16); + } else + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data) +{ + u8 isk[32], imck[60]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)", + data->simck_idx + 1); + + /* + * RFC 4851, Section 5.2: + * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", + * MSK[j], 60) + * S-IMCK[j] = first 40 octets of IMCK[j] + * CMK[j] = last 20 octets of IMCK[j] + */ + + if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); + sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + data->simck_idx++; + os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", + data->simck, EAP_FAST_SIMCK_LEN); + os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", + data->cmk, EAP_FAST_CMK_LEN); + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + u8 ciphers[5] = { + TLS_CIPHER_ANON_DH_AES128_SHA, + TLS_CIPHER_AES128_SHA, + TLS_CIPHER_RSA_DHE_AES128_SHA, + TLS_CIPHER_RC4_SHA, + TLS_CIPHER_NONE + }; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->fast_version = EAP_FAST_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d", + data->force_version); + data->fast_version = data->force_version; + } + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_reset(sm, data); + return NULL; + } + + if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, + ciphers) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher " + "suites"); + eap_fast_reset(sm, data); + return NULL; + } + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_fast_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " + "callback"); + eap_fast_reset(sm, data); + return NULL; + } + + if (sm->pac_opaque_encr_key == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key " + "configured"); + eap_fast_reset(sm, data); + return NULL; + } + os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key, + sizeof(data->pac_opaque_encr)); + + if (sm->eap_fast_a_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured"); + eap_fast_reset(sm, data); + return NULL; + } + data->srv_id = os_malloc(sm->eap_fast_a_id_len); + if (data->srv_id == NULL) { + eap_fast_reset(sm, data); + return NULL; + } + os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); + data->srv_id_len = sm->eap_fast_a_id_len; + + if (sm->eap_fast_a_id_info == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured"); + eap_fast_reset(sm, data); + return NULL; + } + data->srv_id_info = os_strdup(sm->eap_fast_a_id_info); + if (data->srv_id_info == NULL) { + eap_fast_reset(sm, data); + return NULL; + } + + /* PAC-Key lifetime in seconds (hard limit) */ + data->pac_key_lifetime = sm->pac_key_lifetime; + + /* + * PAC-Key refresh time in seconds (soft limit on remaining hard + * limit). The server will generate a new PAC-Key when this number of + * seconds (or fewer) of the lifetime remains. + */ + data->pac_key_refresh_time = sm->pac_key_refresh_time; + + return data; +} + + +static void eap_fast_reset(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + os_free(data->srv_id); + os_free(data->srv_id_info); + os_free(data->key_block_p); + wpabuf_free(data->pending_phase2_resp); + os_free(data->identity); + os_free(data); +} + + +static struct wpabuf * eap_fast_build_start(struct eap_sm *sm, + struct eap_fast_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, + 1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for" + " request"); + eap_fast_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version); + + /* RFC 4851, 4.1.1. Authority ID Data */ + eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); + + eap_fast_state(data, PHASE1); + + return req; +} + + +static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) +{ + char cipher[64]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2"); + + if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher " + "information"); + eap_fast_state(data, FAILURE); + return -1; + } + data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; + + if (data->anon_provisioning) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); + eap_fast_derive_key_provisioning(sm, data); + } else + eap_fast_derive_key_auth(sm, data); + + eap_fast_state(data, PHASE2_START); + + return 0; +} + + +static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm, + struct eap_fast_data *data, + u8 id) +{ + struct wpabuf *req; + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "initialized"); + return NULL; + } + req = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (req == NULL) + return NULL; + + wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req); + return eap_fast_tlv_eap_payload(req); +} + + +static struct wpabuf * eap_fast_build_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data) +{ + struct wpabuf *buf; + struct eap_tlv_result_tlv *result; + struct eap_tlv_crypto_binding_tlv *binding; + + buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding)); + if (buf == NULL) + return NULL; + + if (data->send_new_pac || data->anon_provisioning || + data->phase2_method) + data->final_result = 0; + else + data->final_result = 1; + + if (!data->final_result || data->eap_seq > 1) { + /* Intermediate-Result */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV " + "(status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16( + EAP_TLV_TYPE_MANDATORY | + EAP_TLV_INTERMEDIATE_RESULT_TLV); + result->length = host_to_be16(2); + result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + } + + if (data->final_result) { + /* Result TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV " + "(status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_RESULT_TLV); + result->length = host_to_be16(2); + result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + } + + /* Crypto-Binding TLV */ + binding = wpabuf_put(buf, sizeof(*binding)); + binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV); + binding->length = host_to_be16(sizeof(*binding) - + sizeof(struct eap_tlv_hdr)); + binding->version = EAP_FAST_VERSION; + binding->received_version = data->peer_version; + binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; + if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) { + wpabuf_free(buf); + return NULL; + } + + /* + * RFC 4851, Section 4.2.8: + * The nonce in a request MUST have its least significant bit set to 0. + */ + binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01; + + os_memcpy(data->crypto_binding_nonce, binding->nonce, + sizeof(binding->nonce)); + + /* + * RFC 4851, Section 5.3: + * CMK = CMK[j] + * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV ) + */ + + hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, + (u8 *) binding, sizeof(*binding), + binding->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + binding->version, binding->received_version, + binding->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + binding->nonce, sizeof(binding->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + binding->compound_mac, sizeof(binding->compound_mac)); + + return buf; +} + + +static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_buf, *pac_opaque; + struct wpabuf *buf; + u8 *pos; + size_t buf_len, srv_id_info_len, pac_len; + struct eap_tlv_hdr *pac_tlv; + struct pac_tlv_hdr *pac_info; + struct eap_tlv_result_tlv *result; + struct os_time now; + + if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || + os_get_time(&now) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key", + pac_key, EAP_FAST_PAC_KEY_LEN); + + pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) + + (2 + sm->identity_len) + 8; + pac_buf = os_malloc(pac_len); + if (pac_buf == NULL) + return NULL; + + srv_id_info_len = os_strlen(data->srv_id_info); + + pos = pac_buf; + *pos++ = PAC_OPAQUE_TYPE_KEY; + *pos++ = EAP_FAST_PAC_KEY_LEN; + os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + + *pos++ = PAC_OPAQUE_TYPE_LIFETIME; + *pos++ = 4; + WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); + pos += 4; + + if (sm->identity) { + *pos++ = PAC_OPAQUE_TYPE_IDENTITY; + *pos++ = sm->identity_len; + os_memcpy(pos, sm->identity, sm->identity_len); + pos += sm->identity_len; + } + + pac_len = pos - pac_buf; + while (pac_len % 8) { + *pos++ = PAC_OPAQUE_TYPE_PAD; + pac_len++; + } + + pac_opaque = os_malloc(pac_len + 8); + if (pac_opaque == NULL) { + os_free(pac_buf); + return NULL; + } + if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf, + pac_opaque) < 0) { + os_free(pac_buf); + os_free(pac_opaque); + return NULL; + } + os_free(pac_buf); + + pac_len += 8; + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", + pac_opaque, pac_len); + + buf_len = sizeof(*pac_tlv) + + sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN + + sizeof(struct pac_tlv_hdr) + pac_len + + data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); + buf = wpabuf_alloc(buf_len); + if (buf == NULL) { + os_free(pac_opaque); + return NULL; + } + + /* Result TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + WPA_PUT_BE16((u8 *) &result->tlv_type, + EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV); + WPA_PUT_BE16((u8 *) &result->length, 2); + WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS); + + /* PAC TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV"); + pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); + pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_PAC_TLV); + + /* PAC-Key */ + eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN); + + /* PAC-Opaque */ + eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); + os_free(pac_opaque); + + /* PAC-Info */ + pac_info = wpabuf_put(buf, sizeof(*pac_info)); + pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); + + /* PAC-Lifetime (inside PAC-Info) */ + eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); + wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); + + /* A-ID (inside PAC-Info) */ + eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); + + /* Note: headers may be misaligned after A-ID */ + + /* A-ID-Info (inside PAC-Info) */ + eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, + srv_id_info_len); + + /* PAC-Type (inside PAC-Info) */ + eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); + wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); + + /* Update PAC-Info and PAC TLV Length fields */ + pos = wpabuf_put(buf, 0); + pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); + pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); + + return buf; +} + + +static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_fast_data *data = priv; + struct wpabuf *req = NULL; + struct wpabuf *encr; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_FAST, + data->fast_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, + data->fast_version, id); + } + + switch (data->state) { + case START: + return eap_fast_build_start(sm, data, id); + case PHASE1: + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (eap_fast_phase1_done(sm, data) < 0) + return NULL; + } + break; + case PHASE2_ID: + case PHASE2_METHOD: + req = eap_fast_build_phase2_req(sm, data, id); + break; + case CRYPTO_BINDING: + req = eap_fast_build_crypto_binding(sm, data); + if (data->phase2_method) { + /* + * Include the start of the next EAP method in the + * sequence in the same message with Crypto-Binding to + * save a round-trip. + */ + struct wpabuf *eap; + eap = eap_fast_build_phase2_req(sm, data, id); + req = wpabuf_concat(req, eap); + eap_fast_state(data, PHASE2_METHOD); + } + break; + case REQUEST_PAC: + req = eap_fast_build_pac(sm, data); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + if (req) { + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 " + "TLVs", req); + encr = eap_server_tls_encrypt(sm, &data->ssl, + wpabuf_mhead(req), + wpabuf_len(req)); + wpabuf_free(req); + + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = encr; + } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, + data->fast_version, id); +} + + +static Boolean eap_fast_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + if (data->key_block_p) { + sm->auth_challenge = data->key_block_p->server_challenge; + sm->peer_challenge = data->key_block_p->client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + + return data->phase2_priv == NULL ? -1 : 0; +} + + +static void eap_fast_process_phase2_response(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + struct wpabuf buf; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; + + if (priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); +#ifdef EAP_TNC + if (m && m->vendor == EAP_VENDOR_IETF && + m->method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required " + "TNC negotiation"); + next_type = eap_fast_req_failure(sm, data); + eap_fast_phase2_init(sm, data, next_type); + return; + } +#endif /* EAP_TNC */ + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", + next_type); + } else { + next_type = eap_fast_req_failure(sm, data); + } + eap_fast_phase2_init(sm, data, next_type); + return; + } + + wpabuf_set(&buf, in_data, in_len); + + if (m->check(sm, priv, &buf)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " + "ignore the packet"); + next_type = eap_fast_req_failure(sm, data); + return; + } + + m->process(sm, priv, &buf); + + if (!m->isDone(sm, priv)) + return; + + if (!m->isSuccess(sm, priv)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed"); + next_type = eap_fast_req_failure(sm, data); + eap_fast_phase2_init(sm, data, next_type); + return; + } + + switch (data->state) { + case PHASE2_ID: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + next_type = eap_fast_req_failure(sm, data); + break; + } + + eap_fast_state(data, PHASE2_METHOD); + if (data->anon_provisioning) { + /* + * Only EAP-MSCHAPv2 is allowed for anonymous + * provisioning. + */ + next_type = EAP_TYPE_MSCHAPV2; + sm->user_eap_method_index = 0; + } else { + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + case CRYPTO_BINDING: + eap_fast_update_icmk(sm, data); + eap_fast_state(data, CRYPTO_BINDING); + data->eap_seq++; + next_type = EAP_TYPE_NONE; +#ifdef EAP_TNC + if (sm->tnc && !data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC"); + next_type = EAP_TYPE_TNC; + data->tnc_started = 1; + } +#endif /* EAP_TNC */ + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_fast_phase2_init(sm, data, next_type); +} + + +static void eap_fast_process_phase2_eap(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *in_data, size_t in_len) +{ + struct eap_hdr *hdr; + size_t len; + + hdr = (struct eap_hdr *) in_data; + if (in_len < (int) sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "EAP frame (len=%lu)", (unsigned long) in_len); + eap_fast_req_failure(sm, data); + return; + } + len = be_to_host16(hdr->length); + if (len > in_len) { + wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) in_len, (unsigned long) len); + eap_fast_req_failure(sm, data); + return; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len); + break; + default: + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } +} + + +static int eap_fast_parse_tlvs(u8 *data, size_t data_len, + struct eap_fast_tlv_parse *tlv) +{ + int mandatory, tlv_type, len, res; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + pos = data; + end = data + data_len; + while (pos + 4 < end) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + /* TODO: generate Nak TLV */ + break; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored " + "unknown optional TLV type %d", + tlv_type); + } + } + + pos += len; + } + + return 0; +} + + +static int eap_fast_validate_crypto_binding( + struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b, + size_t bind_len) +{ + u8 cmac[SHA1_MAC_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: " + "Version %d Received Version %d SubType %d", + b->version, b->received_version, b->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + b->nonce, sizeof(b->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + b->compound_mac, sizeof(b->compound_mac)); + + if (b->version != EAP_FAST_VERSION || + b->received_version != EAP_FAST_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version " + "in Crypto-Binding: version %d " + "received_version %d", b->version, + b->received_version); + return -1; + } + + if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in " + "Crypto-Binding: %d", b->subtype); + return -1; + } + + if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 || + (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in " + "Crypto-Binding"); + return -1; + } + + os_memcpy(cmac, b->compound_mac, sizeof(cmac)); + os_memset(b->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for " + "Compound MAC calculation", + (u8 *) b, bind_len); + hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len, + b->compound_mac); + if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) { + wpa_hexdump(MSG_MSGDUMP, + "EAP-FAST: Calculated Compound MAC", + b->compound_mac, sizeof(cmac)); + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not " + "match"); + return -1; + } + + return 0; +} + + +static int eap_fast_pac_type(u8 *pac, size_t len, u16 type) +{ + struct eap_tlv_pac_type_tlv *tlv; + + if (pac == NULL || len != sizeof(*tlv)) + return 0; + + tlv = (struct eap_tlv_pac_type_tlv *) pac; + + return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE && + be_to_host16(tlv->length) == 2 && + be_to_host16(tlv->pac_type) == type; +} + + +static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *in_data, size_t in_len) +{ + struct eap_fast_tlv_parse tlv; + int check_crypto_binding = data->state == CRYPTO_BINDING; + + if (eap_fast_parse_tlvs(in_data, in_len, &tlv) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " + "Phase 2 TLVs"); + return; + } + + if (tlv.result == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated " + "failure"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->state == REQUEST_PAC) { + u16 type, len, res; + if (tlv.pac == NULL || tlv.pac_len < 6) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC " + "Acknowledgement received"); + eap_fast_state(data, FAILURE); + return; + } + + type = WPA_GET_BE16(tlv.pac); + len = WPA_GET_BE16(tlv.pac + 2); + res = WPA_GET_BE16(tlv.pac + 4); + + if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || + res != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not " + "contain acknowledgement"); + eap_fast_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received " + "- PAC provisioning succeeded"); + eap_fast_state(data, (data->anon_provisioning || + data->send_new_pac == 2) ? + FAILURE : SUCCESS); + return; + } + + if (check_crypto_binding) { + if (tlv.crypto_binding == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding " + "TLV received"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->final_result && + tlv.result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " + "without Success Result"); + eap_fast_state(data, FAILURE); + return; + } + + if (!data->final_result && + tlv.iresult != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " + "without intermediate Success Result"); + eap_fast_state(data, FAILURE); + return; + } + + if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding, + tlv.crypto_binding_len)) { + eap_fast_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV " + "received"); + if (data->final_result) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " + "completed successfully"); + } + + if (data->anon_provisioning && + sm->eap_fast_prov != ANON_PROV && + sm->eap_fast_prov != BOTH_PROV) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " + "use unauthenticated provisioning which is " + "disabled"); + eap_fast_state(data, FAILURE); + return; + } + + if (sm->eap_fast_prov != AUTH_PROV && + sm->eap_fast_prov != BOTH_PROV && + tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && + eap_fast_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " + "use authenticated provisioning which is " + "disabled"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->anon_provisioning || + (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && + eap_fast_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC))) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new " + "Tunnel PAC"); + eap_fast_state(data, REQUEST_PAC); + } else if (data->send_new_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered " + "re-keying of Tunnel PAC"); + eap_fast_state(data, REQUEST_PAC); + } else if (data->final_result) + eap_fast_state(data, SUCCESS); + } + + if (tlv.eap_payload_tlv) { + eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + } +} + + +static void eap_fast_process_phase2(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *in_buf) +{ + u8 *in_decrypted; + int len_decrypted; + size_t buf_len; + u8 *in_data; + size_t in_len; + + in_data = wpabuf_mhead(in_buf); + in_len = wpabuf_len(in_buf); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + if (data->pending_phase2_resp) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " + "skip decryption and use old data"); + eap_fast_process_phase2_tlvs( + sm, data, wpabuf_mhead(data->pending_phase2_resp), + wpabuf_len(data->pending_phase2_resp)); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = NULL; + return; + } + + buf_len = in_len; + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf_len += 500; + buf_len *= 3; + in_decrypted = os_malloc(buf_len); + if (in_decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory " + "for decryption"); + return; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " + "data"); + os_free(in_decrypted); + eap_fast_state(data, FAILURE); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", + in_decrypted, len_decrypted); + + eap_fast_process_phase2_tlvs(sm, data, in_decrypted, len_decrypted); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = wpabuf_alloc_copy(in_decrypted, + len_decrypted); + } + + os_free(in_decrypted); +} + + +static int eap_fast_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_fast_data *data = priv; + + data->peer_version = peer_version; + + if (data->force_version >= 0 && peer_version != data->force_version) { + wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced" + " version (forced=%d peer=%d) - reject", + data->force_version, peer_version); + return -1; + } + + if (peer_version < data->fast_version) { + wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->fast_version, peer_version); + data->fast_version = peer_version; + } + + return 0; +} + + +static int eap_fast_process_phase1(struct eap_sm *sm, + struct eap_fast_data *data) +{ + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed"); + eap_fast_state(data, FAILURE); + return -1; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + wpabuf_len(data->ssl.out_buf) > 0) + return 1; + + /* + * Phase 1 was completed with the received message (e.g., when using + * abbreviated handshake), so Phase 2 can be started immediately + * without having to send through an empty message to the peer. + */ + + return eap_fast_phase1_done(sm, data); +} + + +static void eap_fast_process_phase2_start(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 next_type; + + if (data->identity) { + os_free(sm->identity); + sm->identity = data->identity; + data->identity = NULL; + sm->identity_len = data->identity_len; + data->identity_len = 0; + sm->require_identity_match = 1; + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: " + "Phase2 Identity not found " + "in the user database", + sm->identity, sm->identity_len); + next_type = eap_fast_req_failure(sm, data); + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already " + "known - skip Phase 2 Identity Request"); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + } + + eap_fast_state(data, PHASE2_METHOD); + } else { + eap_fast_state(data, PHASE2_ID); + next_type = EAP_TYPE_IDENTITY; + } + + eap_fast_phase2_init(sm, data, next_type); +} + + +static void eap_fast_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_fast_data *data = priv; + + switch (data->state) { + case PHASE1: + if (eap_fast_process_phase1(sm, data)) + break; + + /* fall through to PHASE2_START */ + case PHASE2_START: + eap_fast_process_phase2_start(sm, data); + break; + case PHASE2_ID: + case PHASE2_METHOD: + case CRYPTO_BINDING: + case REQUEST_PAC: + eap_fast_process_phase2(sm, data, data->ssl.in_buf); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_fast_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_fast_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_FAST, eap_fast_process_version, + eap_fast_process_msg) < 0) + eap_fast_state(data, FAILURE); +} + + +static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_FAST_KEY_LEN); + if (eapKeyData == NULL) + return NULL; + + eap_fast_derive_eap_msk(data->simck, eapKeyData); + *len = EAP_FAST_KEY_LEN; + + return eapKeyData; +} + + +static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_EMSK_LEN); + if (eapKeyData == NULL) + return NULL; + + eap_fast_derive_eap_emsk(data->simck, eapKeyData); + *len = EAP_EMSK_LEN; + + return eapKeyData; +} + + +static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_fast_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); + if (eap == NULL) + return -1; + + eap->init = eap_fast_init; + eap->reset = eap_fast_reset; + eap->buildReq = eap_fast_buildReq; + eap->check = eap_fast_check; + eap->process = eap_fast_process; + eap->isDone = eap_fast_isDone; + eap->getKey = eap_fast_getKey; + eap->get_emsk = eap_fast_get_emsk; + eap->isSuccess = eap_fast_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_gpsk.c b/src/eap_server/eap_gpsk.c index 3fecefe1ad18e..c87d4526884af 100644 --- a/contrib/hostapd/eap_gpsk.c +++ b/src/eap_server/eap_gpsk.c @@ -14,10 +14,9 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "eap_i.h" -#include "eap_gpsk_common.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_gpsk_common.h" struct eap_gpsk_data { @@ -72,15 +71,15 @@ static void * eap_gpsk_init(struct eap_sm *sm) { struct eap_gpsk_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = GPSK_1; /* TODO: add support for configuring ID_Server */ - data->id_server = (u8 *) strdup("hostapd"); + data->id_server = (u8 *) os_strdup("hostapd"); if (data->id_server) - data->id_server_len = strlen((char *) data->id_server); + data->id_server_len = os_strlen((char *) data->id_server); data->csuite_count = 0; if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, @@ -107,23 +106,21 @@ static void * eap_gpsk_init(struct eap_sm *sm) static void eap_gpsk_reset(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; - free(data->id_server); - free(data->id_peer); - free(data); + os_free(data->id_server); + os_free(data->id_peer); + os_free(data); } -static u8 * eap_gpsk_build_gpsk_1(struct eap_sm *sm, - struct eap_gpsk_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, + struct eap_gpsk_data *data, u8 id) { - u8 *pos; size_t len; - struct eap_hdr *req; + struct wpabuf *req; wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); - if (hostapd_get_rand(data->rand_server, EAP_GPSK_RAND_LEN)) { + if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); eap_gpsk_state(data, FAILURE); return NULL; @@ -133,8 +130,8 @@ static u8 * eap_gpsk_build_gpsk_1(struct eap_sm *sm, len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 + data->csuite_count * sizeof(struct eap_gpsk_csuite); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqDataLen, - len, EAP_CODE_REQUEST, id, &pos); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " "for request/GPSK-1"); @@ -142,42 +139,34 @@ static u8 * eap_gpsk_build_gpsk_1(struct eap_sm *sm, return NULL; } - *pos++ = EAP_GPSK_OPCODE_GPSK_1; + wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); + wpabuf_put_be16(req, data->id_server_len); + wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); + wpabuf_put_be16(req, + data->csuite_count * sizeof(struct eap_gpsk_csuite)); + wpabuf_put_data(req, data->csuite_list, + data->csuite_count * sizeof(struct eap_gpsk_csuite)); - WPA_PUT_BE16(pos, data->id_server_len); - pos += 2; - if (data->id_server) - memcpy(pos, data->id_server, data->id_server_len); - pos += data->id_server_len; - - memcpy(pos, data->rand_server, EAP_GPSK_RAND_LEN); - pos += EAP_GPSK_RAND_LEN; - - WPA_PUT_BE16(pos, data->csuite_count * sizeof(struct eap_gpsk_csuite)); - pos += 2; - memcpy(pos, data->csuite_list, - data->csuite_count * sizeof(struct eap_gpsk_csuite)); - - return (u8 *) req; + return req; } -static u8 * eap_gpsk_build_gpsk_3(struct eap_sm *sm, - struct eap_gpsk_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, + struct eap_gpsk_data *data, u8 id) { u8 *pos, *start; size_t len, miclen; struct eap_gpsk_csuite *csuite; - struct eap_hdr *req; + struct wpabuf *req; wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqDataLen, - len, EAP_CODE_REQUEST, id, &pos); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " "for request/GPSK-3"); @@ -185,49 +174,42 @@ static u8 * eap_gpsk_build_gpsk_3(struct eap_sm *sm, return NULL; } - *pos++ = EAP_GPSK_OPCODE_GPSK_3; - start = pos; + wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3); + start = wpabuf_put(req, 0); - memcpy(pos, data->rand_peer, EAP_GPSK_RAND_LEN); - pos += EAP_GPSK_RAND_LEN; - memcpy(pos, data->rand_server, EAP_GPSK_RAND_LEN); - pos += EAP_GPSK_RAND_LEN; - WPA_PUT_BE16(pos, data->id_server_len); - pos += 2; - if (data->id_server) - memcpy(pos, data->id_server, data->id_server_len); - pos += data->id_server_len; - csuite = (struct eap_gpsk_csuite *) pos; + wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); + wpabuf_put_be16(req, data->id_server_len); + wpabuf_put_data(req, data->id_server, data->id_server_len); + csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); - pos += sizeof(*csuite); /* no PD_Payload_2 */ - WPA_PUT_BE16(pos, 0); - pos += 2; + wpabuf_put_be16(req, 0); + pos = wpabuf_put(req, miclen); if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, pos - start, pos) < 0) { - free(req); + os_free(req); eap_gpsk_state(data, FAILURE); return NULL; } - return (u8 *) req; + return req; } -static u8 * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_gpsk_data *data = priv; switch (data->state) { case GPSK_1: - return eap_gpsk_build_gpsk_1(sm, data, id, reqDataLen); + return eap_gpsk_build_gpsk_1(sm, data, id); case GPSK_3: - return eap_gpsk_build_gpsk_3(sm, data, id, reqDataLen); + return eap_gpsk_build_gpsk_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq", data->state); @@ -238,14 +220,13 @@ static u8 * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_gpsk_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame"); return TRUE; @@ -268,7 +249,6 @@ static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, struct eap_gpsk_data *data, - u8 *respData, size_t respDataLen, const u8 *payload, size_t payloadlen) { const u8 *pos, *end; @@ -299,14 +279,14 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - free(data->id_peer); - data->id_peer = malloc(alen); + os_free(data->id_peer); + data->id_peer = os_malloc(alen); if (data->id_peer == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " "%d-octet ID_Peer", alen); return; } - memcpy(data->id_peer, pos, alen); + os_memcpy(data->id_peer, pos, alen); data->id_peer_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", data->id_peer, data->id_peer_len); @@ -327,7 +307,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } if (alen != data->id_server_len || - memcmp(pos, data->id_server, alen) != 0) { + os_memcmp(pos, data->id_server, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -341,7 +321,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN); + os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", data->rand_peer, EAP_GPSK_RAND_LEN); pos += EAP_GPSK_RAND_LEN; @@ -352,7 +332,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { + if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " "GPSK-2 did not match"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", @@ -379,7 +359,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) || - memcmp(pos, data->csuite_list, alen) != 0) { + os_memcmp(pos, data->csuite_list, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -395,8 +375,8 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, } csuite = (const struct eap_gpsk_csuite *) pos; for (i = 0; i < data->csuite_count; i++) { - if (memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) == - 0) + if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) + == 0) break; } if (i == data->csuite_count) { @@ -453,7 +433,9 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " - "(left=%d miclen=%d)", end - pos, miclen); + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); eap_gpsk_state(data, FAILURE); return; } @@ -464,7 +446,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (memcmp(mic, pos, miclen) != 0) { + if (os_memcmp(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); @@ -474,8 +456,9 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, pos += miclen; if (pos != end) { - wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra " - "data in the end of GPSK-2", end - pos); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-2", + (unsigned long) (end - pos)); } eap_gpsk_state(data, GPSK_3); @@ -484,7 +467,6 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, struct eap_gpsk_data *data, - u8 *respData, size_t respDataLen, const u8 *payload, size_t payloadlen) { const u8 *pos, *end; @@ -520,7 +502,9 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " - "(left=%d miclen=%d)", end - pos, miclen); + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); eap_gpsk_state(data, FAILURE); return; } @@ -531,7 +515,7 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (memcmp(mic, pos, miclen) != 0) { + if (os_memcmp(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); @@ -541,8 +525,9 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, pos += miclen; if (pos != end) { - wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra " - "data in the end of GPSK-4", end - pos); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-4", + (unsigned long) (end - pos)); } eap_gpsk_state(data, SUCCESS); @@ -550,25 +535,22 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, static void eap_gpsk_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_gpsk_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); if (pos == NULL || len < 1) return; switch (*pos) { case EAP_GPSK_OPCODE_GPSK_2: - eap_gpsk_process_gpsk_2(sm, data, respData, respDataLen, - pos + 1, len - 1); + eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1); break; case EAP_GPSK_OPCODE_GPSK_4: - eap_gpsk_process_gpsk_4(sm, data, respData, respDataLen, - pos + 1, len - 1); + eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1); break; } } @@ -589,10 +571,10 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_MSK_LEN); + key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->msk, EAP_MSK_LEN); + os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -607,10 +589,10 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_EMSK_LEN); + key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->emsk, EAP_EMSK_LEN); + os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/contrib/hostapd/eap_gtc.c b/src/eap_server/eap_gtc.c index 42e4cd35cd748..97e328b83fa6d 100644 --- a/contrib/hostapd/eap_gtc.c +++ b/src/eap_server/eap_gtc.c @@ -14,13 +14,13 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" struct eap_gtc_data { enum { CONTINUE, SUCCESS, FAILURE } state; + int prefix; }; @@ -28,11 +28,20 @@ static void * eap_gtc_init(struct eap_sm *sm) { struct eap_gtc_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; +#ifdef EAP_FAST + if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && + sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } +#endif /* EAP_FAST */ + return data; } @@ -40,22 +49,22 @@ static void * eap_gtc_init(struct eap_sm *sm) static void eap_gtc_reset(struct eap_sm *sm, void *priv) { struct eap_gtc_data *data = priv; - free(data); + os_free(data); } -static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_gtc_data *data = priv; - struct eap_hdr *req; - u8 *pos; - char *msg = "Password"; + struct wpabuf *req; + char *msg; size_t msg_len; - msg_len = strlen(msg); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqDataLen, - msg_len, EAP_CODE_REQUEST, id, &pos); + msg = data->prefix ? "CHALLENGE=Password" : "Password"; + + msg_len = os_strlen(msg); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " "request"); @@ -63,22 +72,21 @@ static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - memcpy(pos, msg, msg_len); + wpabuf_put_data(req, msg, msg_len); data->state = CONTINUE; - return (u8 *) req; + return req; } static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); return TRUE; @@ -89,12 +97,81 @@ static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, static void eap_gtc_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_gtc_data *data = priv; const u8 *pos; size_t rlen; + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen); + if (pos == NULL || rlen < 1) + return; /* Should not happen - frame already validated */ + + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); + +#ifdef EAP_FAST + if (data->prefix) { + const u8 *pos2, *end; + /* "RESPONSE=<user>\0<password>" */ + if (rlen < 10) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response " + "for EAP-FAST prefix"); + data->state = FAILURE; + return; + } + + end = pos + rlen; + pos += 9; + pos2 = pos; + while (pos2 < end && *pos2) + pos2++; + if (pos2 == end) { + wpa_printf(MSG_DEBUG, "EAP-GTC: No password in " + "response to EAP-FAST prefix"); + data->state = FAILURE; + return; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user", + pos, pos2 - pos); + if (sm->identity && sm->require_identity_match && + (pos2 - pos != (int) sm->identity_len || + os_memcmp(pos, sm->identity, sm->identity_len))) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did " + "not match with required Identity"); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected " + "identity", + sm->identity, sm->identity_len); + data->state = FAILURE; + return; + } else { + os_free(sm->identity); + sm->identity_len = pos2 - pos; + sm->identity = os_malloc(sm->identity_len); + if (sm->identity == NULL) { + data->state = FAILURE; + return; + } + os_memcpy(sm->identity, pos, sm->identity_len); + } + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + data->state = FAILURE; + return; + } + + pos = pos2 + 1; + rlen = end - pos; + wpa_hexdump_ascii_key(MSG_MSGDUMP, + "EAP-GTC: Response password", + pos, rlen); + } +#endif /* EAP_FAST */ + if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not " @@ -103,15 +180,8 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, return; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, - respData, respDataLen, &rlen); - if (pos == NULL || rlen < 1) - return; /* Should not happen - frame already validated */ - - wpa_hexdump_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); - if (rlen != sm->user->password_len || - memcmp(pos, sm->user->password, rlen) != 0) { + os_memcmp(pos, sm->user->password, rlen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); data->state = FAILURE; } else { diff --git a/contrib/hostapd/eap_i.h b/src/eap_server/eap_i.h index 85b2c2d2fac87..61f564d5c49f3 100644 --- a/contrib/hostapd/eap_i.h +++ b/src/eap_server/eap_i.h @@ -1,6 +1,6 @@ /* * hostapd / EAP Authenticator state machine internal structures (RFC 4137) - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,7 +15,9 @@ #ifndef EAP_I_H #define EAP_I_H -#include "eap.h" +#include "wpabuf.h" +#include "eap_server/eap.h" +#include "eap_common/eap_common.h" /* RFC 4137 - EAP Standalone Authenticator */ @@ -34,13 +36,12 @@ struct eap_method { void * (*initPickUp)(struct eap_sm *sm); void (*reset)(struct eap_sm *sm, void *priv); - u8 * (*buildReq)(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen); + struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id); int (*getTimeout)(struct eap_sm *sm, void *priv); Boolean (*check)(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen); + struct wpabuf *respData); void (*process)(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen); + struct wpabuf *respData); Boolean (*isDone)(struct eap_sm *sm, void *priv); u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, @@ -104,34 +105,19 @@ struct eap_sm { EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, - EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD + EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD, + EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, + EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, + EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 } EAP_state; /* Constants */ int MaxRetrans; - /* Lower layer to standalone authenticator variables */ - /* eapResp: eapol_sm->be_auth.eapResp */ - /* portEnabled: eapol_sm->portEnabled */ - /* eapRestart: eapol_sm->auth_pae.eapRestart */ - u8 *eapRespData; - size_t eapRespDataLen; - int retransWhile; - int eapSRTT; - int eapRTTVAR; - - /* Standalone authenticator to lower layer variables */ - /* eapReq: eapol_sm->be_auth.eapReq */ - /* eapNoReq: eapol_sm->be_auth.eapNoReq */ - /* eapSuccess: eapol_sm->eapSuccess */ - /* eapFail: eapol_sm->eapFail */ - /* eapTimeout: eapol_sm->eapTimeout */ - u8 *eapReqData; - size_t eapReqDataLen; - u8 *eapKeyData; /* also eapKeyAvailable (boolean) */ - size_t eapKeyDataLen; - - /* Standalone authenticator state machine local variables */ + struct eap_eapol_interface eap_if; + + /* Full authenticator state machine local variables */ /* Long-term (maintained betwen packets) */ EapType currentMethod; @@ -140,8 +126,7 @@ struct eap_sm { METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END } methodState; int retransCount; - u8 *lastReqData; - size_t lastReqDataLen; + struct wpabuf *lastReqData; int methodTimeout; /* Short-term (not maintained between packets) */ @@ -152,41 +137,56 @@ struct eap_sm { u32 respVendorMethod; Boolean ignore; enum { - DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE + DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, + DECISION_PASSTHROUGH } decision; /* Miscellaneous variables */ 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, *msg_ctx; struct eapol_callbacks *eapol_cb; void *eap_method_priv; u8 *identity; size_t identity_len; + /* Whether Phase 2 method should validate identity match */ + int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ struct eap_user *user; int user_eap_method_index; int init_phase2; void *ssl_ctx; - enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; void *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; + int eap_server; int num_rounds; enum { METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT } method_pending; + + u8 *auth_challenge; + u8 *peer_challenge; + + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + enum { + NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV + } eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + struct wpabuf *assoc_wps_ie; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2); -void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len); -const u8 * eap_hdr_validate(int vendor, EapType eap_type, - const u8 *msg, size_t msglen, size_t *plen); -struct eap_hdr * eap_msg_alloc(int vendor, EapType type, size_t *len, - size_t payload_len, u8 code, u8 identifier, - u8 **payload); +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); #endif /* EAP_I_H */ diff --git a/contrib/hostapd/eap_identity.c b/src/eap_server/eap_identity.c index ab6f26b1fea53..cd8da2a632b9b 100644 --- a/contrib/hostapd/eap_identity.c +++ b/src/eap_server/eap_identity.c @@ -14,7 +14,6 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" @@ -29,7 +28,7 @@ static void * eap_identity_init(struct eap_sm *sm) { struct eap_identity_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; @@ -52,16 +51,15 @@ static void * eap_identity_initPickUp(struct eap_sm *sm) static void eap_identity_reset(struct eap_sm *sm, void *priv) { struct eap_identity_data *data = priv; - free(data); + os_free(data); } -static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv, + u8 id) { struct eap_identity_data *data = priv; - struct eap_hdr *req; - u8 *pos; + struct wpabuf *req; const char *req_data; size_t req_data_len; @@ -72,8 +70,8 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, req_data = NULL; req_data_len = 0; } - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, reqDataLen, - req_data_len, EAP_CODE_REQUEST, id, &pos); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " "memory for request"); @@ -81,21 +79,20 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - if (req_data) - memcpy(pos, req_data, req_data_len); + wpabuf_put_data(req, req_data, req_data_len); - return (u8 *) req; + return req; } static Boolean eap_identity_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, - respData, respDataLen, &len); + respData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); return TRUE; @@ -106,14 +103,14 @@ static Boolean eap_identity_check(struct eap_sm *sm, void *priv, static void eap_identity_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_identity_data *data = priv; const u8 *pos; size_t len; if (data->pick_up) { - if (eap_identity_check(sm, data, respData, respDataLen)) { + if (eap_identity_check(sm, data, respData)) { wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " "up already started negotiation"); data->state = FAILURE; @@ -123,17 +120,19 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, - respData, respDataLen, &len); + respData, &len); if (pos == NULL) return; /* Should not happen - frame already validated */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); - free(sm->identity); - sm->identity = malloc(len); + if (sm->identity) + sm->update_user = TRUE; + os_free(sm->identity); + sm->identity = os_malloc(len ? len : 1); if (sm->identity == NULL) { data->state = FAILURE; } else { - memcpy(sm->identity, pos, len); + os_memcpy(sm->identity, pos, len); sm->identity_len = len; data->state = SUCCESS; } diff --git a/src/eap_server/eap_ikev2.c b/src/eap_server/eap_ikev2.c new file mode 100644 index 0000000000000..06074ee28fb05 --- /dev/null +++ b/src/eap_server/eap_ikev2.c @@ -0,0 +1,538 @@ +/* + * EAP-IKEv2 server (RFC 5106) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_common/eap_ikev2_common.h" +#include "ikev2.h" + + +struct eap_ikev2_data { + struct ikev2_initiator_data ikev2; + enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; + int keys_ready; + u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; + int keymat_ok; +}; + + +static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr, + size_t IDr_len, + size_t *secret_len) +{ + struct eap_sm *sm = ctx; + + if (IDr == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default " + "to user identity from EAP-Identity"); + IDr = sm->identity; + IDr_len = sm->identity_len; + } + + if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL || + sm->user->password == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found"); + return NULL; + } + + *secret_len = sm->user->password_len; + return sm->user->password; +} + + +static const char * eap_ikev2_state_txt(int state) +{ + switch (state) { + case MSG: + return "MSG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_ikev2_state(struct eap_ikev2_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", + eap_ikev2_state_txt(data->state), + eap_ikev2_state_txt(state)); + data->state = state; +} + + +static void * eap_ikev2_init(struct eap_sm *sm) +{ + struct eap_ikev2_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = MSG; + data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->ikev2.state = SA_INIT; + data->ikev2.peer_auth = PEER_AUTH_SECRET; + data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); + if (data->ikev2.key_pad == NULL) + goto failed; + data->ikev2.key_pad_len = 21; + + /* TODO: make proposals configurable */ + data->ikev2.proposal.proposal_num = 1; + data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96; + data->ikev2.proposal.prf = PRF_HMAC_SHA1; + data->ikev2.proposal.encr = ENCR_AES_CBC; + data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; + + data->ikev2.IDi = (u8 *) os_strdup("hostapd"); + data->ikev2.IDi_len = 7; + + data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; + data->ikev2.cb_ctx = sm; + + return data; + +failed: + ikev2_initiator_deinit(&data->ikev2); + os_free(data); + return NULL; +} + + +static void eap_ikev2_reset(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + ikev2_initiator_deinit(&data->ikev2); + os_free(data); +} + + +static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen, icv_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request"); + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= IKEV2_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + plen += 4; + if (data->keys_ready) { + const struct ikev2_integ_alg *integ; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " + "Data"); + flags |= IKEV2_FLAGS_ICV_INCLUDED; + integ = ikev2_get_integ(data->ikev2.proposal.integ); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot generate ICV"); + return NULL; + } + icv_len = integ->hash_len; + + plen += icv_len; + } + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + const u8 *msg = wpabuf_head(req); + size_t len = wpabuf_len(req); + ikev2_integ_hash(data->ikev2.proposal.integ, + data->ikev2.keys.SK_ai, + data->ikev2.keys.SK_integ_len, + msg, len, wpabuf_put(req, icv_len)); + } + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + } else { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_ikev2_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_ikev2_data *data = priv; + + switch (data->state) { + case MSG: + if (data->out_buf == NULL) { + data->out_buf = ikev2_initiator_build(&data->ikev2); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " + "generate IKEv2 message"); + return NULL; + } + data->out_used = 0; + } + /* pass through */ + case WAIT_FRAG_ACK: + return eap_ikev2_build_msg(data, id); + case FRAG_ACK: + return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST); + default: + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in " + "buildReq", data->state); + return NULL; + } +} + + +static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, + &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_ikev2_process_icv(struct eap_ikev2_data *data, + const struct wpabuf *respData, + u8 flags, const u8 *pos, const u8 **end) +{ + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + int icv_len = eap_ikev2_validate_icv( + data->ikev2.proposal.integ, &data->ikev2.keys, 0, + respData, pos, *end); + if (icv_len < 0) + return -1; + /* Hide Integrity Checksum Data from further processing */ + *end -= icv_len; + } else if (data->keys_ready) { + wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " + "included integrity checksum"); + return -1; + } + + return 0; +} + + +static int eap_ikev2_process_cont(struct eap_ikev2_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); + eap_ikev2_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_ikev2_process_fragment(struct eap_ikev2_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " + "a fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " + "message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static int eap_ikev2_server_keymat(struct eap_ikev2_data *data) +{ + if (eap_ikev2_derive_keymat( + data->ikev2.proposal.prf, &data->ikev2.keys, + data->ikev2.i_nonce, data->ikev2.i_nonce_len, + data->ikev2.r_nonce, data->ikev2.r_nonce_len, + data->keymat) < 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive " + "key material"); + return -1; + } + data->keymat_ok = 1; + return 0; +} + + +static void eap_ikev2_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_ikev2_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 flags; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, + &len); + if (pos == NULL) + return; /* Should not happen; message already verified */ + + start = pos; + end = start + len; + + if (len == 0) { + /* fragment ack */ + flags = 0; + } else + flags = *pos++; + + if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) { + eap_ikev2_state(data, FAIL); + return; + } + + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); + eap_ikev2_state(data, FAIL); + return; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + eap_ikev2_state(data, FAIL); + return; + } + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len != 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " + "in WAIT_FRAG_ACK state"); + eap_ikev2_state(data, FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); + eap_ikev2_state(data, MSG); + return; + } + + if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { + eap_ikev2_state(data, FAIL); + return; + } + + if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { + if (eap_ikev2_process_fragment(data, flags, message_length, + pos, end - pos) < 0) + eap_ikev2_state(data, FAIL); + else + eap_ikev2_state(data, FRAG_ACK); + return; + } else if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); + data->state = MSG; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) { + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + eap_ikev2_state(data, FAIL); + return; + } + + switch (data->ikev2.state) { + case SA_AUTH: + /* SA_INIT was sent out, so message have to be + * integrity protected from now on. */ + data->keys_ready = 1; + break; + case IKEV2_DONE: + if (data->state == FAIL) + break; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed " + "successfully"); + if (eap_ikev2_server_keymat(data)) + break; + eap_ikev2_state(data, DONE); + break; + default: + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE || data->state == FAIL; +} + + +static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE && data->ikev2.state == IKEV2_DONE && + data->keymat_ok; +} + + +static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key) { + os_memcpy(key, data->keymat, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + } + + return key; +} + + +static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key) { + os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + } + + return key; +} + + +int eap_server_ikev2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IKEV2, + "IKEV2"); + if (eap == NULL) + return -1; + + eap->init = eap_ikev2_init; + eap->reset = eap_ikev2_reset; + eap->buildReq = eap_ikev2_buildReq; + eap->check = eap_ikev2_check; + eap->process = eap_ikev2_process; + eap->isDone = eap_ikev2_isDone; + eap->getKey = eap_ikev2_getKey; + eap->isSuccess = eap_ikev2_isSuccess; + eap->get_emsk = eap_ikev2_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_md5.c b/src/eap_server/eap_md5.c index 234a319b69099..dee2dc5a013e5 100644 --- a/contrib/hostapd/eap_md5.c +++ b/src/eap_server/eap_md5.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,11 +14,9 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" -#include "md5.h" -#include "crypto.h" +#include "eap_common/chap.h" #define CHALLENGE_LEN 16 @@ -33,7 +31,7 @@ static void * eap_md5_init(struct eap_sm *sm) { struct eap_md5_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CONTINUE; @@ -45,25 +43,23 @@ static void * eap_md5_init(struct eap_sm *sm) static void eap_md5_reset(struct eap_sm *sm, void *priv) { struct eap_md5_data *data = priv; - free(data); + os_free(data); } -static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_md5_data *data = priv; - struct eap_hdr *req; - u8 *pos; + struct wpabuf *req; - if (hostapd_get_rand(data->challenge, CHALLENGE_LEN)) { + if (os_get_random(data->challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); data->state = FAILURE; return NULL; } - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqDataLen, - 1 + CHALLENGE_LEN, EAP_CODE_REQUEST, id, &pos); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " "request"); @@ -71,29 +67,29 @@ static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - *pos++ = CHALLENGE_LEN; - memcpy(pos, data->challenge, CHALLENGE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", pos, CHALLENGE_LEN); + wpabuf_put_u8(req, CHALLENGE_LEN); + wpabuf_put_data(req, data->challenge, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge, + CHALLENGE_LEN); data->state = CONTINUE; - return (u8 *) req; + return req; } static Boolean eap_md5_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); return TRUE; } - if (*pos != MD5_MAC_LEN || 1 + MD5_MAC_LEN > len) { + if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " "(response_len=%d payload_len=%lu", *pos, (unsigned long) len); @@ -105,14 +101,12 @@ static Boolean eap_md5_check(struct eap_sm *sm, void *priv, static void eap_md5_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_md5_data *data = priv; - struct eap_hdr *resp; const u8 *pos; - const u8 *addr[3]; - size_t len[3], plen; - u8 hash[MD5_MAC_LEN]; + size_t plen; + u8 hash[CHAP_MD5_LEN], id; if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { @@ -122,24 +116,18 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, return; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, - respData, respDataLen, &plen); - if (pos == NULL || *pos != MD5_MAC_LEN || plen < 1 + MD5_MAC_LEN) + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen); + if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN) return; /* Should not happen - frame already validated */ pos++; /* Skip response len */ - wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); - - resp = (struct eap_hdr *) respData; - addr[0] = &resp->identifier; - len[0] = 1; - addr[1] = sm->user->password; - len[1] = sm->user->password_len; - addr[2] = data->challenge; - len[2] = CHALLENGE_LEN; - md5_vector(3, addr, len, hash); - - if (memcmp(hash, pos, MD5_MAC_LEN) == 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); + + id = eap_get_id(respData); + chap_md5(id, sm->user->password, sm->user->password_len, + data->challenge, CHALLENGE_LEN, hash); + + if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); data->state = SUCCESS; } else { diff --git a/contrib/hostapd/eap_methods.c b/src/eap_server/eap_methods.c index 671d5486c3166..4092d675b34fc 100644 --- a/contrib/hostapd/eap_methods.c +++ b/src/eap_server/eap_methods.c @@ -14,7 +14,7 @@ #include "includes.h" -#include "hostapd.h" +#include "common.h" #include "eap_i.h" #include "eap_methods.h" @@ -23,12 +23,12 @@ static struct eap_method *eap_methods; /** - * eap_sm_get_eap_methods - Get EAP method based on type number + * eap_server_get_eap_method - Get EAP method based on type number * @vendor: EAP Vendor-Id (0 = IETF) * @method: EAP type number * Returns: Pointer to EAP method or %NULL if not found */ -const struct eap_method * eap_sm_get_eap_methods(int vendor, EapType method) +const struct eap_method * eap_server_get_eap_method(int vendor, EapType method) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { @@ -40,7 +40,7 @@ const struct eap_method * eap_sm_get_eap_methods(int vendor, EapType method) /** - * eap_get_type - Get EAP type for the given EAP method name + * eap_server_get_type - Get EAP type for the given EAP method name * @name: EAP method name, e.g., TLS * @vendor: Buffer for returning EAP Vendor-Id * Returns: EAP method type or %EAP_TYPE_NONE if not found @@ -48,11 +48,11 @@ const struct eap_method * eap_sm_get_eap_methods(int vendor, EapType method) * This function maps EAP type names into EAP type numbers based on the list of * EAP methods included in the build. */ -EapType eap_get_type(const char *name, int *vendor) +EapType eap_server_get_type(const char *name, int *vendor) { struct eap_method *m; for (m = eap_methods; m; m = m->next) { - if (strcmp(m->name, name) == 0) { + if (os_strcmp(m->name, name) == 0) { *vendor = m->vendor; return m->method; } @@ -68,7 +68,7 @@ EapType eap_get_type(const char *name, int *vendor) * EAP_SERVER_METHOD_INTERFACE_VERSION) * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) * @method: EAP type number (EAP_TYPE_*) - * name: Name of the method (e.g., "TLS") + * @name: Name of the method (e.g., "TLS") * Returns: Allocated EAP method structure or %NULL on failure * * The returned structure should be freed with eap_server_method_free() when it @@ -78,7 +78,7 @@ struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name) { struct eap_method *eap; - eap = wpa_zalloc(sizeof(*eap)); + eap = os_zalloc(sizeof(*eap)); if (eap == NULL) return NULL; eap->version = version; @@ -95,7 +95,7 @@ struct eap_method * eap_server_method_alloc(int version, int vendor, */ void eap_server_method_free(struct eap_method *method) { - free(method); + os_free(method); } @@ -119,7 +119,7 @@ int eap_server_method_register(struct eap_method *method) for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || - strcmp(m->name, method->name) == 0) + os_strcmp(m->name, method->name) == 0) return -2; last = m; } @@ -212,6 +212,13 @@ int eap_server_register_methods(void) } #endif /* EAP_AKA */ +#ifdef EAP_AKA_PRIME + if (ret == 0) { + int eap_server_aka_prime_register(void); + ret = eap_server_aka_prime_register(); + } +#endif /* EAP_AKA_PRIME */ + #ifdef EAP_PAX if (ret == 0) { int eap_server_pax_register(void); @@ -247,6 +254,34 @@ int eap_server_register_methods(void) } #endif /* EAP_VENDOR_TEST */ +#ifdef EAP_FAST + if (ret == 0) { + int eap_server_fast_register(void); + ret = eap_server_fast_register(); + } +#endif /* EAP_FAST */ + +#ifdef EAP_WSC + if (ret == 0) { + int eap_server_wsc_register(void); + ret = eap_server_wsc_register(); + } +#endif /* EAP_WSC */ + +#ifdef EAP_IKEV2 + if (ret == 0) { + int eap_server_ikev2_register(void); + ret = eap_server_ikev2_register(); + } +#endif /* EAP_IKEV2 */ + +#ifdef EAP_TNC + if (ret == 0) { + int eap_server_tnc_register(void); + ret = eap_server_tnc_register(); + } +#endif /* EAP_TNC */ + return ret; } diff --git a/contrib/hostapd/eap_methods.h b/src/eap_server/eap_methods.h index cec8a570e2a62..0fd53909ff4ba 100644 --- a/contrib/hostapd/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -15,35 +15,15 @@ #ifndef EAP_METHODS_H #define EAP_METHODS_H -const struct eap_method * eap_sm_get_eap_methods(int vendor, EapType method); +const struct eap_method * eap_server_get_eap_method(int vendor, + EapType method); struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name); void eap_server_method_free(struct eap_method *method); int eap_server_method_register(struct eap_method *method); -#ifdef EAP_SERVER - -EapType eap_get_type(const char *name, int *vendor); +EapType eap_server_get_type(const char *name, int *vendor); int eap_server_register_methods(void); void eap_server_unregister_methods(void); -#else /* EAP_SERVER */ - -static inline EapType eap_get_type(const char *name, int *vendor) -{ - *vendor = EAP_VENDOR_IETF; - return EAP_TYPE_NONE; -} - -static inline int eap_server_register_methods(void) -{ - return 0; -} - -static inline void eap_server_unregister_methods(void) -{ -} - -#endif /* EAP_SERVER */ - #endif /* EAP_METHODS_H */ diff --git a/contrib/hostapd/eap_mschapv2.c b/src/eap_server/eap_mschapv2.c index bbd819bbe12bf..20e7adee6f958 100644 --- a/contrib/hostapd/eap_mschapv2.c +++ b/src/eap_server/eap_mschapv2.c @@ -14,7 +14,6 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" #include "ms_funcs.h" @@ -51,6 +50,8 @@ struct eap_mschapv2_hdr { struct eap_mschapv2_data { u8 auth_challenge[CHALLENGE_LEN]; + int auth_challenge_from_tls; + u8 *peer_challenge; u8 auth_response[20]; enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; u8 resp_mschapv2_id; @@ -63,11 +64,27 @@ static void * eap_mschapv2_init(struct eap_sm *sm) { struct eap_mschapv2_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CHALLENGE; + if (sm->auth_challenge) { + os_memcpy(data->auth_challenge, sm->auth_challenge, + CHALLENGE_LEN); + data->auth_challenge_from_tls = 1; + } + + if (sm->peer_challenge) { + data->peer_challenge = os_malloc(CHALLENGE_LEN); + if (data->peer_challenge == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->peer_challenge, sm->peer_challenge, + CHALLENGE_LEN); + } + return data; } @@ -75,30 +92,33 @@ static void * eap_mschapv2_init(struct eap_sm *sm) static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; - free(data); + if (data == NULL) + return; + + os_free(data->peer_challenge); + os_free(data); } -static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_mschapv2_build_challenge( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { - struct eap_hdr *req; + struct wpabuf *req; struct eap_mschapv2_hdr *ms; - u8 *pos; char *name = "hostapd"; /* TODO: make this configurable */ size_t ms_len; - if (hostapd_get_rand(data->auth_challenge, CHALLENGE_LEN)) { + if (!data->auth_challenge_from_tls && + os_get_random(data->auth_challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " "data"); data->state = FAILURE; return NULL; } - ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + strlen(name); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqDataLen, - ms_len, EAP_CODE_REQUEST, id, &pos); + ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -106,37 +126,37 @@ static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, return NULL; } - ms = (struct eap_mschapv2_hdr *) pos; + ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_CHALLENGE; ms->mschapv2_id = id; WPA_PUT_BE16(ms->ms_length, ms_len); - pos = (u8 *) (ms + 1); - *pos++ = CHALLENGE_LEN; - memcpy(pos, data->auth_challenge, CHALLENGE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", pos, - CHALLENGE_LEN); - pos += CHALLENGE_LEN; - memcpy(pos, name, strlen(name)); + wpabuf_put_u8(req, CHALLENGE_LEN); + if (!data->auth_challenge_from_tls) + wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); + else + wpabuf_put(req, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", + data->auth_challenge, CHALLENGE_LEN); + wpabuf_put_data(req, name, os_strlen(name)); - return (u8 *) req; + return req; } -static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_mschapv2_build_success_req( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { - struct eap_hdr *req; + struct wpabuf *req; struct eap_mschapv2_hdr *ms; - u8 *pos, *msg; + u8 *msg; char *message = "OK"; size_t ms_len; ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + - strlen(message); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqDataLen, - ms_len, EAP_CODE_REQUEST, id, &pos); + os_strlen(message); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -144,44 +164,42 @@ static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, return NULL; } - ms = (struct eap_mschapv2_hdr *) pos; + ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_SUCCESS; ms->mschapv2_id = data->resp_mschapv2_id; WPA_PUT_BE16(ms->ms_length, ms_len); - - msg = pos = (u8 *) (ms + 1); - *pos++ = 'S'; - *pos++ = '='; - pos += wpa_snprintf_hex_uppercase((char *) pos, - sizeof(data->auth_response) * 2 + 1, - data->auth_response, - sizeof(data->auth_response)); - *pos++ = ' '; - *pos++ = 'M'; - *pos++ = '='; - memcpy(pos, message, strlen(message)); + msg = (u8 *) (ms + 1); + + wpabuf_put_u8(req, 'S'); + wpabuf_put_u8(req, '='); + wpa_snprintf_hex_uppercase( + wpabuf_put(req, sizeof(data->auth_response) * 2), + sizeof(data->auth_response) * 2 + 1, + data->auth_response, sizeof(data->auth_response)); + wpabuf_put_u8(req, ' '); + wpabuf_put_u8(req, 'M'); + wpabuf_put_u8(req, '='); + wpabuf_put_data(req, message, os_strlen(message)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", msg, ms_len - sizeof(*ms)); - return (u8 *) req; + return req; } -static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_mschapv2_build_failure_req( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) { - struct eap_hdr *req; + struct wpabuf *req; struct eap_mschapv2_hdr *ms; - u8 *pos; char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " "M=FAILED"; size_t ms_len; - ms_len = sizeof(*ms) + strlen(message); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqDataLen, - ms_len, EAP_CODE_REQUEST, id, &pos); + ms_len = sizeof(*ms) + os_strlen(message); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -189,34 +207,32 @@ static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, return NULL; } - ms = (struct eap_mschapv2_hdr *) pos; + ms = wpabuf_put(req, sizeof(*ms)); ms->op_code = MSCHAPV2_OP_FAILURE; ms->mschapv2_id = data->resp_mschapv2_id; WPA_PUT_BE16(ms->ms_length, ms_len); - memcpy((u8 *) (ms + 1), message, strlen(message)); + wpabuf_put_data(req, message, os_strlen(message)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", - (u8 *) message, strlen(message)); + (u8 *) message, os_strlen(message)); - return (u8 *) req; + return req; } -static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, + u8 id) { struct eap_mschapv2_data *data = priv; switch (data->state) { case CHALLENGE: - return eap_mschapv2_build_challenge(sm, data, id, reqDataLen); + return eap_mschapv2_build_challenge(sm, data, id); case SUCCESS_REQ: - return eap_mschapv2_build_success_req(sm, data, id, - reqDataLen); + return eap_mschapv2_build_success_req(sm, data, id); case FAILURE_REQ: - return eap_mschapv2_build_failure_req(sm, data, id, - reqDataLen); + return eap_mschapv2_build_failure_req(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " "buildReq", data->state); @@ -227,15 +243,15 @@ static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_mschapv2_data *data = priv; struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); return TRUE; @@ -270,7 +286,7 @@ static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, static void eap_mschapv2_process_response(struct eap_sm *sm, struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos, *end, *peer_challenge, *nt_response, *name; @@ -280,8 +296,8 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, const u8 *username, *user; size_t username_len, user_len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ @@ -292,8 +308,8 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { - wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", - respData, respDataLen); + wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", + respData); data->state = FAILURE; return; } @@ -307,6 +323,11 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, name = pos; name_len = end - name; + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " + "Peer-Challenge"); + peer_challenge = data->peer_challenge; + } wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); @@ -337,7 +358,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } if (username_len != user_len || - memcmp(username, user, username_len) != 0) { + os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " "name", username, username_len); @@ -364,7 +385,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, expected); } - if (memcmp(nt_response, expected, 24) == 0) { + if (os_memcmp(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; @@ -376,23 +397,16 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, * not be saved. */ if (sm->user->password_hash) { pw_hash = sm->user->password; - generate_authenticator_response_pwhash( - sm->user->password, peer_challenge, - data->auth_challenge, username, username_len, - nt_response, data->auth_response); } else { nt_password_hash(sm->user->password, sm->user->password_len, pw_hash_buf); pw_hash = pw_hash_buf; - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - data->auth_challenge, - username, username_len, - nt_response, - data->auth_response); } + generate_authenticator_response_pwhash( + pw_hash, peer_challenge, data->auth_challenge, + username, username_len, nt_response, + data->auth_response); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, nt_response, data->master_key); @@ -410,14 +424,14 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, static void eap_mschapv2_process_success_resp(struct eap_sm *sm, struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ @@ -437,14 +451,14 @@ static void eap_mschapv2_process_success_resp(struct eap_sm *sm, static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, - respData, respDataLen, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ @@ -463,7 +477,7 @@ static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, static void eap_mschapv2_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_mschapv2_data *data = priv; @@ -475,15 +489,13 @@ static void eap_mschapv2_process(struct eap_sm *sm, void *priv, switch (data->state) { case CHALLENGE: - eap_mschapv2_process_response(sm, data, respData, respDataLen); + eap_mschapv2_process_response(sm, data, respData); break; case SUCCESS_REQ: - eap_mschapv2_process_success_resp(sm, data, respData, - respDataLen); + eap_mschapv2_process_success_resp(sm, data, respData); break; case FAILURE_REQ: - eap_mschapv2_process_failure_resp(sm, data, respData, - respDataLen); + eap_mschapv2_process_failure_resp(sm, data, respData); break; default: wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " @@ -509,12 +521,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = 2 * MSCHAPV2_KEY_LEN; - key = malloc(*len); + key = os_malloc(*len); if (key == NULL) return NULL; - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 0); + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 0); + MSCHAPV2_KEY_LEN, 1, 1); wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); return key; diff --git a/contrib/hostapd/eap_pax.c b/src/eap_server/eap_pax.c index 8daa8d3adb043..1dc023b699551 100644 --- a/contrib/hostapd/eap_pax.c +++ b/src/eap_server/eap_pax.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-PAX (RFC 4746) server - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,10 +14,9 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "eap_i.h" -#include "eap_pax_common.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_pax_common.h" /* * Note: only PAX_STD subprotocol is currently supported @@ -52,7 +51,7 @@ static void * eap_pax_init(struct eap_sm *sm) { struct eap_pax_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = PAX_STD_1; @@ -69,28 +68,29 @@ static void * eap_pax_init(struct eap_sm *sm) static void eap_pax_reset(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; - free(data->cid); - free(data); + os_free(data->cid); + os_free(data); } -static u8 * eap_pax_build_std_1(struct eap_sm *sm, - struct eap_pax_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, + struct eap_pax_data *data, u8 id) { - struct eap_pax_hdr *req; + struct wpabuf *req; + struct eap_pax_hdr *pax; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); - if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) { + if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); data->state = FAILURE; return NULL; } - *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " "request"); @@ -98,44 +98,40 @@ static u8 * eap_pax_build_std_1(struct eap_sm *sm, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PAX; - req->op_code = EAP_PAX_OP_STD_1; - req->flags = 0; - req->mac_id = data->mac_id; - req->dh_group_id = EAP_PAX_DH_GROUP_NONE; - req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; - pos = (u8 *) (req + 1); - *pos++ = 0; - *pos++ = EAP_PAX_RAND_LEN; - memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN); + pax = wpabuf_put(req, sizeof(*pax)); + pax->op_code = EAP_PAX_OP_STD_1; + pax->flags = 0; + pax->mac_id = data->mac_id; + pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; + pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + + wpabuf_put_be16(req, EAP_PAX_RAND_LEN); + wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", - pos, EAP_PAX_RAND_LEN); - pos += EAP_PAX_RAND_LEN; + data->rand.r.x, EAP_PAX_RAND_LEN); + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, (u8 *) "", 0, - (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - pos += EAP_PAX_ICV_LEN; - return (u8 *) req; + return req; } -static u8 * eap_pax_build_std_3(struct eap_sm *sm, - struct eap_pax_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, + struct eap_pax_data *data, u8 id) { - struct eap_pax_hdr *req; + struct wpabuf *req; + struct eap_pax_hdr *pax; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); - *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " "request"); @@ -143,18 +139,15 @@ static u8 * eap_pax_build_std_3(struct eap_sm *sm, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PAX; - req->op_code = EAP_PAX_OP_STD_3; - req->flags = 0; - req->mac_id = data->mac_id; - req->dh_group_id = EAP_PAX_DH_GROUP_NONE; - req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; - pos = (u8 *) (req + 1); - *pos++ = 0; - *pos++ = EAP_PAX_MAC_LEN; + pax = wpabuf_put(req, sizeof(*pax)); + pax->op_code = EAP_PAX_OP_STD_3; + pax->flags = 0; + pax->mac_id = data->mac_id; + pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; + pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + + wpabuf_put_be16(req, EAP_PAX_MAC_LEN); + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, NULL, 0, pos); @@ -164,26 +157,25 @@ static u8 * eap_pax_build_std_3(struct eap_sm *sm, /* Optional ADE could be added here, if needed */ + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - pos += EAP_PAX_ICV_LEN; - return (u8 *) req; + return req; } -static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_pax_data *data = priv; switch (data->state) { case PAX_STD_1: - return eap_pax_build_std_1(sm, data, id, reqDataLen); + return eap_pax_build_std_1(sm, data, id); case PAX_STD_3: - return eap_pax_build_std_3(sm, data, id, reqDataLen); + return eap_pax_build_std_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", data->state); @@ -194,21 +186,23 @@ static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_pax_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_pax_data *data = priv; struct eap_pax_hdr *resp; - size_t len; + const u8 *pos; + size_t len, mlen; u8 icvbuf[EAP_PAX_ICV_LEN], *icv; - resp = (struct eap_pax_hdr *) respData; - if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX || - (len = ntohs(resp->length)) > respDataLen || - len < sizeof(*resp) + EAP_PAX_ICV_LEN) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp)) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); return TRUE; } + mlen = sizeof(struct eap_hdr) + 1 + len; + resp = (struct eap_pax_hdr *) pos; + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " "flags 0x%x mac_id 0x%x dh_group_id 0x%x " "public_key_id 0x%x", @@ -273,12 +267,13 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); return TRUE; } - icv = respData + len - EAP_PAX_ICV_LEN; + icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, - icvbuf); - if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + wpabuf_mhead(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, icvbuf); + if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -292,10 +287,11 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, static void eap_pax_process_std_2(struct eap_sm *sm, struct eap_pax_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_pax_hdr *resp; - u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; + u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; + const u8 *pos; size_t len, left; int i; @@ -304,44 +300,47 @@ static void eap_pax_process_std_2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); - resp = (struct eap_pax_hdr *) respData; - len = ntohs(resp->length); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) + return; + + resp = (struct eap_pax_hdr *) pos; pos = (u8 *) (resp + 1); left = len - sizeof(*resp); if (left < 2 + EAP_PAX_RAND_LEN || - ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) { + WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); return; } pos += 2; left -= 2; - memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); + os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", data->rand.r.y, EAP_PAX_RAND_LEN); pos += EAP_PAX_RAND_LEN; left -= EAP_PAX_RAND_LEN; - if (left < 2 || (size_t) 2 + ((pos[0] << 8) | pos[1]) > left) { + if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); return; } - data->cid_len = (pos[0] << 8) | pos[1]; - free(data->cid); - data->cid = malloc(data->cid_len); + data->cid_len = WPA_GET_BE16(pos); + os_free(data->cid); + data->cid = os_malloc(data->cid_len); if (data->cid == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " "CID"); return; } - memcpy(data->cid, pos + 2, data->cid_len); + os_memcpy(data->cid, pos + 2, data->cid_len); pos += 2 + data->cid_len; left -= 2 + data->cid_len; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", (u8 *) data->cid, data->cid_len); if (left < 2 + EAP_PAX_MAC_LEN || - ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) { + WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); return; } @@ -385,7 +384,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, data->state = FAILURE; return; } - memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); + os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); if (eap_pax_initial_key_derivation(data->mac_id, data->ak, data->rand.e, data->mk, data->ck, @@ -401,7 +400,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, data->rand.r.x, EAP_PAX_RAND_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, mac); - if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { + if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", @@ -420,8 +419,10 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); - if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + wpabuf_head(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, + icvbuf); + if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -441,7 +442,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, static void eap_pax_process_ack(struct eap_sm *sm, struct eap_pax_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { if (data->state != PAX_STD_3) return; @@ -453,10 +454,12 @@ static void eap_pax_process_ack(struct eap_sm *sm, static void eap_pax_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_pax_data *data = priv; struct eap_pax_hdr *resp; + const u8 *pos; + size_t len; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " @@ -465,14 +468,18 @@ static void eap_pax_process(struct eap_sm *sm, void *priv, return; } - resp = (struct eap_pax_hdr *) respData; + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp)) + return; + + resp = (struct eap_pax_hdr *) pos; switch (resp->op_code) { case EAP_PAX_OP_STD_2: - eap_pax_process_std_2(sm, data, respData, respDataLen); + eap_pax_process_std_2(sm, data, respData); break; case EAP_PAX_OP_ACK: - eap_pax_process_ack(sm, data, respData, respDataLen); + eap_pax_process_ack(sm, data, respData); break; } } @@ -493,7 +500,7 @@ static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_MSK_LEN); + key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; @@ -514,7 +521,7 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_EMSK_LEN); + key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; diff --git a/src/eap_server/eap_peap.c b/src/eap_server/eap_peap.c new file mode 100644 index 0000000000000..4b2d5a5c83ff6 --- /dev/null +++ b/src/eap_server/eap_peap.c @@ -0,0 +1,1421 @@ +/* + * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_peap_common.h" +#include "tls.h" +#include "tncs.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_reset(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID, + PHASE2_METHOD, PHASE2_SOH, + PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE + } state; + + int peap_version; + int recv_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int force_version; + struct wpabuf *pending_phase2_resp; + enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; + int crypto_binding_sent; + int crypto_binding_used; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + u8 *phase2_key; + size_t phase2_key_len; + struct wpabuf *soh_response; +}; + + +static const char * eap_peap_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE1_ID2: + return "PHASE1_ID2"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case PHASE2_SOH: + return "PHASE2_SOH"; + case PHASE2_TLV: + return "PHASE2_TLV"; + case SUCCESS_REQ: + return "SUCCESS_REQ"; + case FAILURE_REQ: + return "FAILURE_REQ"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_peap_state(struct eap_peap_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", + eap_peap_state_txt(data->state), + eap_peap_state_txt(state)); + data->state = state; +} + + +static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + struct eap_tlv_hdr *tlv; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + tlv = wpabuf_put(e, sizeof(*tlv)); + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(wpabuf_len(buf)); + wpabuf_put_buf(e, buf); + wpabuf_free(buf); + return e; +} + + +static void eap_peap_req_success(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ) { + eap_peap_state(data, FAILURE); + return; + } + + if (data->peap_version == 0) { + data->tlv_request = TLV_REQ_SUCCESS; + eap_peap_state(data, PHASE2_TLV); + } else { + eap_peap_state(data, SUCCESS_REQ); + } +} + + +static void eap_peap_req_failure(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ || + data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) { + eap_peap_state(data, FAILURE); + return; + } + + if (data->peap_version == 0) { + data->tlv_request = TLV_REQ_FAILURE; + eap_peap_state(data, PHASE2_TLV); + } else { + eap_peap_state(data, FAILURE_REQ); + } +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->peap_version = EAP_PEAP_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", + data->force_version); + data->peap_version = data->force_version; + } + data->state = START; + data->crypto_binding = OPTIONAL_BINDING; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_reset(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + wpabuf_free(data->pending_phase2_resp); + os_free(data->phase2_key); + wpabuf_free(data->soh_response); + os_free(data); +} + + +static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, + struct eap_peap_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" + " request"); + eap_peap_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version); + + eap_peap_state(data, PHASE1); + + return req; +} + + +static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf, *encr_req; + const u8 *req; + size_t req_len; + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready"); + return NULL; + } + buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (data->peap_version >= 2 && buf) + buf = eap_peapv2_tlv_eap_payload(buf); + if (buf == NULL) + return NULL; + + req = wpabuf_head(buf); + req_len = wpabuf_len(buf); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + req, req_len); + + if (data->peap_version == 0 && + data->phase2_method->method != EAP_TYPE_TLV) { + req += sizeof(struct eap_hdr); + req_len -= sizeof(struct eap_hdr); + } + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + wpabuf_free(buf); + + return encr_req; +} + + +#ifdef EAP_TNC +static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf1, *buf, *encr_req; + const u8 *req; + size_t req_len; + + buf1 = tncs_build_soh_request(); + if (buf1 == NULL) + return NULL; + + buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1), + EAP_CODE_REQUEST, id); + if (buf == NULL) { + wpabuf_free(buf1); + return NULL; + } + wpabuf_put_buf(buf, buf1); + wpabuf_free(buf1); + + req = wpabuf_head(buf); + req_len = wpabuf_len(buf); + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data", + req, req_len); + + req += sizeof(struct eap_hdr); + req_len -= sizeof(struct eap_hdr); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + wpabuf_free(buf); + + return encr_req; +} +#endif /* EAP_TNC */ + + +static void eap_peap_get_isk(struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_key == NULL) + return; + + key_len = data->phase2_key_len; + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, data->phase2_key, key_len); +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", + EAP_TLS_KEY_LEN); + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + eap_peap_get_isk(data, isk, sizeof(isk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + os_free(tk); + + /* TODO: fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf, *encr_req; + size_t len; + + len = 6; /* Result TLV */ + if (data->crypto_binding != NO_BINDING) + len += 60; /* Cryptobinding TLV */ +#ifdef EAP_TNC + if (data->soh_response) + len += wpabuf_len(data->soh_response); +#endif /* EAP_TNC */ + + buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + EAP_CODE_REQUEST, id); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, 0x80); /* Mandatory */ + wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV); + /* Length */ + wpabuf_put_be16(buf, 2); + /* Status */ + wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? + EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); + + if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && + data->crypto_binding != NO_BINDING) { + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + +#ifdef EAP_TNC + if (data->soh_response) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " + "Response TLV"); + wpabuf_put_buf(buf, data->soh_response); + wpabuf_free(data->soh_response); + data->soh_response = NULL; + } +#endif /* EAP_TNC */ + + if (eap_peap_derive_cmk(sm, data) < 0 || + os_get_random(data->binding_nonce, 32)) { + wpabuf_free(buf); + return NULL; + } + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ + wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", + data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", + mac, SHA1_MAC_LEN); + data->crypto_binding_sent = 1; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", + buf); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_head(buf), + wpabuf_len(buf)); + wpabuf_free(buf); + + return encr_req; +} + + +static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id, int success) +{ + struct wpabuf *encr_req; + size_t req_len; + struct eap_hdr *hdr; + + req_len = sizeof(*hdr); + hdr = os_zalloc(req_len); + if (hdr == NULL) + return NULL; + + hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + hdr->identifier = id; + hdr->length = host_to_be16(req_len); + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + (u8 *) hdr, req_len); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, (u8 *) hdr, req_len); + os_free(hdr); + + return encr_req; +} + + +static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_peap_data *data = priv; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); + } + + switch (data->state) { + case START: + return eap_peap_build_start(sm, data, id); + case PHASE1: + case PHASE1_ID2: + if (data->peap_version < 2 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " + "starting Phase2"); + eap_peap_state(data, PHASE2_START); + } + break; + case PHASE2_ID: + case PHASE2_METHOD: + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_req(sm, data, id); + break; +#ifdef EAP_TNC + case PHASE2_SOH: + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_soh(sm, data, id); + break; +#endif /* EAP_TNC */ + case PHASE2_TLV: + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_tlv(sm, data, id); + break; + case SUCCESS_REQ: + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 1); + break; + case FAILURE_REQ: + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 0); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); +} + + +static Boolean eap_peap_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + return 0; +} + + +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 1) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data", + buf, 61); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + +static void eap_peap_process_phase2_tlv(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + const u8 *pos; + size_t left; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; + int tlv_type, mandatory, tlv_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header"); + return; + } + + /* Parse TLVs */ + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if ((size_t) tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + + /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding_sent) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + eap_peap_state(data, FAILURE); + return; + } + data->crypto_binding_used = 1; + } else if (!crypto_tlv && data->crypto_binding_sent && + data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + eap_peap_state(data, FAILURE); + return; + } + + if (result_tlv) { + int status; + const char *requested; + + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + eap_peap_state(data, FAILURE); + return; + } + requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" : + "Failure"; + status = WPA_GET_BE16(result_tlv); + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " + "- requested %s", requested); + if (data->tlv_request == TLV_REQ_SUCCESS) + eap_peap_state(data, SUCCESS); + else + eap_peap_state(data, FAILURE); + + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " + "- requested %s", requested); + eap_peap_state(data, FAILURE); + } else { + wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result " + "Status %d", status); + eap_peap_state(data, FAILURE); + } + } +} + + +#ifdef EAP_TNC +static void eap_peap_process_phase2_soh(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + const u8 *pos, *vpos; + size_t left; + const u8 *soh_tlv = NULL; + size_t soh_tlv_len = 0; + int tlv_type, mandatory, tlv_len, vtlv_len; + u8 next_type; + u32 vendor_id; + + pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP " + "Extensions Method header - skip TNC"); + goto auth_method; + } + + /* Parse TLVs */ + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if ((size_t) tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + switch (tlv_type) { + case EAP_TLV_VENDOR_SPECIFIC_TLV: + if (tlv_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short " + "vendor specific TLV (len=%d)", + (int) tlv_len); + eap_peap_state(data, FAILURE); + return; + } + + vendor_id = WPA_GET_BE32(pos); + if (vendor_id != EAP_VENDOR_MICROSOFT) { + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + break; + } + + vpos = pos + 4; + mandatory = !!(vpos[0] & 0x80); + tlv_type = vpos[0] & 0x3f; + tlv_type = (tlv_type << 8) | vpos[1]; + vtlv_len = ((int) vpos[2] << 8) | vpos[3]; + vpos += 4; + if (vpos + vtlv_len > pos + left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV " + "underrun"); + eap_peap_state(data, FAILURE); + return; + } + + if (tlv_type == 1) { + soh_tlv = vpos; + soh_tlv_len = vtlv_len; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV " + "Type %d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + + /* Process supported TLVs */ + if (soh_tlv) { + int failure = 0; + wpabuf_free(data->soh_response); + data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len, + &failure); + if (failure) { + eap_peap_state(data, FAILURE); + return; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received"); + eap_peap_state(data, FAILURE); + return; + } + +auth_method: + eap_peap_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + eap_peap_phase2_init(sm, data, next_type); +} +#endif /* EAP_TNC */ + + +static void eap_peap_process_phase2_response(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + u8 next_type = EAP_TYPE_NONE; + const struct eap_hdr *hdr; + const u8 *pos; + size_t left; + + if (data->state == PHASE2_TLV) { + eap_peap_process_phase2_tlv(sm, data, in_data); + return; + } + +#ifdef EAP_TNC + if (data->state == PHASE2_SOH) { + eap_peap_process_phase2_soh(sm, data, in_data); + return; + } +#endif /* EAP_TNC */ + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = wpabuf_head(in_data); + pos = (const u8 *) (hdr + 1); + + if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = wpabuf_len(in_data) - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", + next_type); + } else { + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + } + eap_peap_phase2_init(sm, data, next_type); + return; + } + + if (data->phase2_method->check(sm, data->phase2_priv, in_data)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " + "ignore the packet"); + return; + } + + data->phase2_method->process(sm, data->phase2_priv, in_data); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = wpabuf_dup(in_data); + } + + if (!data->phase2_method->isDone(sm, data->phase2_priv)) + return; + + if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + eap_peap_phase2_init(sm, data, next_type); + return; + } + + os_free(data->phase2_key); + if (data->phase2_method->getKey) { + data->phase2_key = data->phase2_method->getKey( + sm, data->phase2_priv, &data->phase2_key_len); + if (data->phase2_key == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " + "failed"); + eap_peap_req_failure(sm, data); + eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + return; + } + } + + switch (data->state) { + case PHASE1_ID2: + case PHASE2_ID: + case PHASE2_SOH: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + break; + } + +#ifdef EAP_TNC + if (data->state != PHASE2_SOH && sm->tnc && + data->peap_version == 0) { + eap_peap_state(data, PHASE2_SOH); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " + "TNC (NAP SOH)"); + next_type = EAP_TYPE_NONE; + break; + } +#endif /* EAP_TNC */ + + eap_peap_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + eap_peap_req_success(sm, data); + next_type = EAP_TYPE_NONE; + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_peap_phase2_init(sm, data, next_type); +} + + +static void eap_peap_process_phase2(struct eap_sm *sm, + struct eap_peap_data *data, + const struct wpabuf *respData, + struct wpabuf *in_buf) +{ + struct wpabuf *in_decrypted; + int len_decrypted; + const struct eap_hdr *hdr; + size_t buf_len, len; + u8 *in_data; + size_t in_len; + + in_data = wpabuf_mhead(in_buf); + in_len = wpabuf_len(in_buf); + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + if (data->pending_phase2_resp) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " + "skip decryption and use old data"); + eap_peap_process_phase2_response(sm, data, + data->pending_phase2_resp); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = NULL; + return; + } + + buf_len = in_len; + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf_len += 500; + buf_len *= 3; + in_decrypted = wpabuf_alloc(buf_len); + if (in_decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " + "for decryption"); + return; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + wpabuf_mhead(in_decrypted), + buf_len); + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " + "data"); + wpabuf_free(in_decrypted); + eap_peap_state(data, FAILURE); + return; + } + wpabuf_put(in_decrypted, len_decrypted); + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted); + + hdr = wpabuf_head(in_decrypted); + + if (data->peap_version == 0 && data->state != PHASE2_TLV) { + const struct eap_hdr *resp; + struct eap_hdr *nhdr; + struct wpabuf *nbuf = + wpabuf_alloc(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + if (nbuf == NULL) { + wpabuf_free(in_decrypted); + return; + } + + resp = wpabuf_head(respData); + nhdr = wpabuf_put(nbuf, sizeof(*nhdr)); + nhdr->code = resp->code; + nhdr->identifier = resp->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + wpabuf_put_buf(nbuf, in_decrypted); + wpabuf_free(in_decrypted); + + in_decrypted = nbuf; + } else if (data->peap_version >= 2) { + struct eap_tlv_hdr *tlv; + struct wpabuf *nmsg; + + if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " + "EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + tlv = wpabuf_mhead(in_decrypted); + if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) != + EAP_TLV_EAP_PAYLOAD_TLV) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + if (sizeof(*tlv) + be_to_host16(tlv->length) > + wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " + "length"); + wpabuf_free(in_decrypted); + return; + } + hdr = (struct eap_hdr *) (tlv + 1); + if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " + "EAP packet in EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + + nmsg = wpabuf_alloc(be_to_host16(hdr->length)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return; + } + + wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + hdr = wpabuf_head(in_decrypted); + if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + eap_peap_req_failure(sm, data); + return; + } + len = be_to_host16(hdr->length); + if (len > wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) wpabuf_len(in_decrypted), + (unsigned long) len); + wpabuf_free(in_decrypted); + eap_peap_req_failure(sm, data); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_peap_process_phase2_response(sm, data, in_decrypted); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->state == SUCCESS_REQ) { + eap_peap_state(data, SUCCESS); + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + os_free(in_decrypted); +} + + +static int eap_peapv2_start_phase2(struct eap_sm *sm, + struct eap_peap_data *data) +{ + struct wpabuf *buf, *buf2; + int res; + + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " + "payload in the same message"); + eap_peap_state(data, PHASE1_ID2); + if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY)) + return -1; + + /* TODO: which Id to use here? */ + buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6); + if (buf == NULL) + return -1; + + buf2 = eap_peapv2_tlv_eap_payload(buf); + if (buf2 == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2); + + buf = wpabuf_alloc(data->ssl.tls_out_limit); + if (buf == NULL) { + wpabuf_free(buf2); + return -1; + } + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + wpabuf_head(buf2), wpabuf_len(buf2), + wpabuf_put(buf, 0), + data->ssl.tls_out_limit); + wpabuf_free(buf2); + + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " + "data"); + wpabuf_free(buf); + return -1; + } + + wpabuf_put(buf, res); + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request", + buf); + + /* Append TLS data into the pending buffer after the Server Finished */ + if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(buf)) < 0) { + wpabuf_free(buf); + return -1; + } + wpabuf_put_buf(data->ssl.out_buf, buf); + wpabuf_free(buf); + + return 0; +} + + +static int eap_peap_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_peap_data *data = priv; + + data->recv_version = peer_version; + if (data->force_version >= 0 && peer_version != data->force_version) { + wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" + " version (forced=%d peer=%d) - reject", + data->force_version, peer_version); + return -1; + } + if (peer_version < data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->peap_version, peer_version); + data->peap_version = peer_version; + } + + return 0; +} + + +static void eap_peap_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_peap_data *data = priv; + + switch (data->state) { + case PHASE1: + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { + eap_peap_state(data, FAILURE); + break; + } + + if (data->peap_version >= 2 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (eap_peapv2_start_phase2(sm, data)) { + eap_peap_state(data, FAILURE); + break; + } + } + break; + case PHASE2_START: + eap_peap_state(data, PHASE2_ID); + eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + break; + case PHASE1_ID2: + case PHASE2_ID: + case PHASE2_METHOD: + case PHASE2_SOH: + case PHASE2_TLV: + eap_peap_process_phase2(sm, data, respData, data->ssl.in_buf); + break; + case SUCCESS_REQ: + eap_peap_state(data, SUCCESS); + break; + case FAILURE_REQ: + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_peap_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_peap_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_PEAP, eap_peap_process_version, + eap_peap_process_msg) < 0) + eap_peap_state(data, FAILURE); +} + + +static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + if (data->crypto_binding_used) { + u8 csk[128]; + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + eapKeyData = os_malloc(EAP_TLS_KEY_LEN); + if (eapKeyData) { + os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN); + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive " + "key"); + } + + return eapKeyData; + } + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_peap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_peap_init; + eap->reset = eap_peap_reset; + eap->buildReq = eap_peap_buildReq; + eap->check = eap_peap_check; + eap->process = eap_peap_process; + eap->isDone = eap_peap_isDone; + eap->getKey = eap_peap_getKey; + eap->isSuccess = eap_peap_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_psk.c b/src/eap_server/eap_psk.c index 7408a522e328e..c68d4c34d4514 100644 --- a/contrib/hostapd/eap_psk.c +++ b/src/eap_server/eap_psk.c @@ -17,11 +17,10 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "eap_i.h" +#include "eap_server/eap_i.h" #include "aes_wrap.h" -#include "eap_psk_common.h" +#include "eap_common/eap_psk_common.h" struct eap_psk_data { @@ -40,7 +39,7 @@ static void * eap_psk_init(struct eap_sm *sm) { struct eap_psk_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = PSK_1; @@ -54,19 +53,20 @@ static void * eap_psk_init(struct eap_sm *sm) static void eap_psk_reset(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; - free(data->id_p); - free(data); + os_free(data->id_p); + os_free(data); } -static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, + struct eap_psk_data *data, u8 id) { - struct eap_psk_hdr_1 *req; + struct wpabuf *req; + struct eap_psk_hdr_1 *psk; wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); - if (hostapd_get_rand(data->rand_s, EAP_PSK_RAND_LEN)) { + if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); data->state = FAILURE; return NULL; @@ -74,8 +74,9 @@ static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", data->rand_s, EAP_PSK_RAND_LEN); - *reqDataLen = sizeof(*req) + data->id_s_len; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*psk) + data->id_s_len, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " "request"); @@ -83,29 +84,27 @@ static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PSK; - req->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ - memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - memcpy((u8 *) (req + 1), data->id_s, data->id_s_len); + psk = wpabuf_put(req, sizeof(*psk)); + psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ + os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + wpabuf_put_data(req, data->id_s, data->id_s_len); - return (u8 *) req; + return req; } -static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, + struct eap_psk_data *data, u8 id) { - struct eap_psk_hdr_3 *req; + struct wpabuf *req; + struct eap_psk_hdr_3 *psk; u8 *buf, *pchannel, nonce[16]; size_t buflen; wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); - *reqDataLen = sizeof(*req) + 4 + 16 + 1; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " "request"); @@ -113,58 +112,61 @@ static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PSK; - req->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */ - memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + psk = wpabuf_put(req, sizeof(*psk)); + psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */ + os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ buflen = data->id_s_len + EAP_PSK_RAND_LEN; - buf = malloc(buflen); - if (buf == NULL) { - free(req); - data->state = FAILURE; - return NULL; - } - memcpy(buf, data->id_s, data->id_s_len); - memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); - omac1_aes_128(data->ak, buf, buflen, req->mac_s); - free(buf); - - eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, - data->emsk); + buf = os_malloc(buflen); + if (buf == NULL) + goto fail; + + os_memcpy(buf, data->id_s, data->id_s_len); + os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) + goto fail; + os_free(buf); + + if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, + data->emsk)) + goto fail; wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); - memset(nonce, 0, sizeof(nonce)); - pchannel = (u8 *) (req + 1); - memcpy(pchannel, nonce + 12, 4); - memset(pchannel + 4, 0, 16); /* Tag */ + os_memset(nonce, 0, sizeof(nonce)); + pchannel = wpabuf_put(req, 4 + 16 + 1); + os_memcpy(pchannel, nonce + 12, 4); + os_memset(pchannel + 4, 0, 16); /* Tag */ pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", pchannel, 4 + 16 + 1); - aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), (u8 *) req, 22, - pchannel + 4 + 16, 1, pchannel + 4); + if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(req), 22, + pchannel + 4 + 16, 1, pchannel + 4)) + goto fail; wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", pchannel, 4 + 16 + 1); - return (u8 *) req; + return req; + +fail: + wpabuf_free(req); + data->state = FAILURE; + return NULL; } -static u8 * eap_psk_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_psk_data *data = priv; switch (data->state) { case PSK_1: - return eap_psk_build_1(sm, data, id, reqDataLen); + return eap_psk_build_1(sm, data, id); case PSK_3: - return eap_psk_build_3(sm, data, id, reqDataLen); + return eap_psk_build_3(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", data->state); @@ -175,21 +177,19 @@ static u8 * eap_psk_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_psk_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_psk_data *data = priv; - struct eap_psk_hdr *resp; size_t len; u8 t; + const u8 *pos; - resp = (struct eap_psk_hdr *) respData; - if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PSK || - (len = ntohs(resp->length)) > respDataLen || - len < sizeof(*resp)) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); return TRUE; } - t = EAP_PSK_FLAGS_GET_T(resp->flags); + t = EAP_PSK_FLAGS_GET_T(*pos); wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); @@ -217,31 +217,37 @@ static Boolean eap_psk_check(struct eap_sm *sm, void *priv, static void eap_psk_process_2(struct eap_sm *sm, struct eap_psk_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_psk_hdr_2 *resp; + const struct eap_psk_hdr_2 *resp; u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; - size_t len, left, buflen; + size_t left, buflen; int i; + const u8 *cpos; if (data->state != PSK_1) return; wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); - resp = (struct eap_psk_hdr_2 *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - left = len - sizeof(*resp); + cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, + &left); + if (cpos == NULL || left < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return; + } + resp = (const struct eap_psk_hdr_2 *) cpos; + cpos = (const u8 *) (resp + 1); + left -= sizeof(*resp); - free(data->id_p); - data->id_p = malloc(left); + os_free(data->id_p); + data->id_p = os_malloc(left); if (data->id_p == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " "ID_P"); return; } - memcpy(data->id_p, pos, left); + os_memcpy(data->id_p, cpos, left); data->id_p_len = left; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", data->id_p, data->id_p_len); @@ -281,32 +287,39 @@ static void eap_psk_process_2(struct eap_sm *sm, data->state = FAILURE; return; } - eap_psk_key_setup(sm->user->password, data->ak, data->kdk); + if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) { + data->state = FAILURE; + return; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", resp->rand_p, EAP_PSK_RAND_LEN); - memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; - buf = malloc(buflen); + buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; return; } - memcpy(buf, data->id_p, data->id_p_len); + os_memcpy(buf, data->id_p, data->id_p_len); pos = buf + data->id_p_len; - memcpy(pos, data->id_s, data->id_s_len); + os_memcpy(pos, data->id_s, data->id_s_len); pos += data->id_s_len; - memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(pos, data->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, mac); - free(buf); + os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, mac)) { + os_free(buf); + data->state = FAILURE; + return; + } + os_free(buf); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); - if (memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { + if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", mac, EAP_PSK_MAC_LEN); @@ -320,20 +333,26 @@ static void eap_psk_process_2(struct eap_sm *sm, static void eap_psk_process_4(struct eap_sm *sm, struct eap_psk_data *data, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_psk_hdr_4 *resp; - u8 *pos, *decrypted, nonce[16], *tag; + const struct eap_psk_hdr_4 *resp; + u8 *decrypted, nonce[16]; size_t left; + const u8 *pos, *tag; if (data->state != PSK_3) return; wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); - resp = (struct eap_psk_hdr_4 *) respData; - pos = (u8 *) (resp + 1); - left = ntohs(resp->length) - sizeof(*resp); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left); + if (pos == NULL || left < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return; + } + resp = (const struct eap_psk_hdr_4 *) pos; + pos = (const u8 *) (resp + 1); + left -= sizeof(*resp); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); @@ -349,23 +368,24 @@ static void eap_psk_process_4(struct eap_sm *sm, return; } - memset(nonce, 0, 12); - memcpy(nonce + 12, pos, 4); + os_memset(nonce, 0, 12); + os_memcpy(nonce + 12, pos, 4); pos += 4; left -= 4; tag = pos; pos += 16; left -= 16; - decrypted = malloc(left); + decrypted = os_malloc(left); if (decrypted == NULL) return; - memcpy(decrypted, pos, left); + os_memcpy(decrypted, pos, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), - respData, 22, decrypted, left, tag)) { + wpabuf_head(respData), 22, decrypted, left, + tag)) { wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); - free(decrypted); + os_free(decrypted); data->state = FAILURE; return; } @@ -387,15 +407,16 @@ static void eap_psk_process_4(struct eap_sm *sm, data->state = FAILURE; break; } - free(decrypted); + os_free(decrypted); } static void eap_psk_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_psk_data *data = priv; - struct eap_psk_hdr *resp; + const u8 *pos; + size_t len; if (sm->user == NULL || sm->user->password == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not " @@ -404,14 +425,16 @@ static void eap_psk_process(struct eap_sm *sm, void *priv, return; } - resp = (struct eap_psk_hdr *) respData; + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); + if (pos == NULL || len < 1) + return; - switch (EAP_PSK_FLAGS_GET_T(resp->flags)) { + switch (EAP_PSK_FLAGS_GET_T(*pos)) { case 1: - eap_psk_process_2(sm, data, respData, respDataLen); + eap_psk_process_2(sm, data, respData); break; case 3: - eap_psk_process_4(sm, data, respData, respDataLen); + eap_psk_process_4(sm, data, respData); break; } } @@ -432,10 +455,10 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_MSK_LEN); + key = os_malloc(EAP_MSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->msk, EAP_MSK_LEN); + os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -450,10 +473,10 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_EMSK_LEN); + key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->emsk, EAP_EMSK_LEN); + os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/contrib/hostapd/eap_sake.c b/src/eap_server/eap_sake.c index e031f29fb436d..ce4848f85e44c 100644 --- a/contrib/hostapd/eap_sake.c +++ b/src/eap_server/eap_sake.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-SAKE (RFC 4763) server - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,10 +14,9 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "eap_i.h" -#include "eap_sake_common.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_sake_common.h" struct eap_sake_data { @@ -70,12 +69,12 @@ static void * eap_sake_init(struct eap_sm *sm) { struct eap_sake_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = CHALLENGE; - if (hostapd_get_rand(&data->session_id, 1)) { + if (os_get_random(&data->session_id, 1)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); os_free(data); return NULL; @@ -84,9 +83,9 @@ static void * eap_sake_init(struct eap_sm *sm) data->session_id); /* TODO: add support for configuring SERVERID */ - data->serverid = (u8 *) strdup("hostapd"); + data->serverid = (u8 *) os_strdup("hostapd"); if (data->serverid) - data->serverid_len = strlen((char *) data->serverid); + data->serverid_len = os_strlen((char *) data->serverid); return data; } @@ -101,79 +100,73 @@ static void eap_sake_reset(struct eap_sm *sm, void *priv) } -static u8 * eap_sake_build_msg(struct eap_sake_data *data, u8 **payload, - int id, size_t *length, u8 subtype) +static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, + u8 id, size_t length, u8 subtype) { - struct eap_sake_hdr *req; - u8 *msg; + struct eap_sake_hdr *sake; + struct wpabuf *msg; + size_t plen; - *length += sizeof(struct eap_sake_hdr); + plen = sizeof(struct eap_sake_hdr) + length; - msg = wpa_zalloc(*length); + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, + EAP_CODE_REQUEST, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " "request"); return NULL; } - req = (struct eap_sake_hdr *) msg; - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons((u16) *length); - req->type = EAP_TYPE_SAKE; - req->version = EAP_SAKE_VERSION; - req->session_id = data->session_id; - req->subtype = subtype; - *payload = (u8 *) (req + 1); + sake = wpabuf_put(msg, sizeof(*sake)); + sake->version = EAP_SAKE_VERSION; + sake->session_id = data->session_id; + sake->subtype = subtype; return msg; } -static u8 * eap_sake_build_identity(struct eap_sm *sm, - struct eap_sake_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) { - u8 *msg, *pos; + struct wpabuf *msg; + size_t plen; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); - *reqDataLen = 4; + plen = 4; if (data->serverid) - *reqDataLen += 2 + data->serverid_len; - msg = eap_sake_build_msg(data, &pos, id, reqDataLen, - EAP_SAKE_SUBTYPE_IDENTITY); + plen += 2 + data->serverid_len; + msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); - *pos++ = EAP_SAKE_AT_PERM_ID_REQ; - *pos++ = 4; - *pos++ = 0; - *pos++ = 0; + eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); if (data->serverid) { wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - *pos++ = EAP_SAKE_AT_SERVERID; - *pos++ = 2 + data->serverid_len; - os_memcpy(pos, data->serverid, data->serverid_len); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + data->serverid, data->serverid_len); } return msg; } -static u8 * eap_sake_build_challenge(struct eap_sm *sm, - struct eap_sake_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) { - u8 *msg, *pos; + struct wpabuf *msg; + size_t plen; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); - if (hostapd_get_rand(data->rand_s, EAP_SAKE_RAND_LEN)) { + if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); data->state = FAILURE; return NULL; @@ -181,43 +174,39 @@ static u8 * eap_sake_build_challenge(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - *reqDataLen = 2 + EAP_SAKE_RAND_LEN; + plen = 2 + EAP_SAKE_RAND_LEN; if (data->serverid) - *reqDataLen += 2 + data->serverid_len; - msg = eap_sake_build_msg(data, &pos, id, reqDataLen, - EAP_SAKE_SUBTYPE_CHALLENGE); + plen += 2 + data->serverid_len; + msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S"); - *pos++ = EAP_SAKE_AT_RAND_S; - *pos++ = 2 + EAP_SAKE_RAND_LEN; - os_memcpy(pos, data->rand_s, EAP_SAKE_RAND_LEN); - pos += EAP_SAKE_RAND_LEN; + eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, + data->rand_s, EAP_SAKE_RAND_LEN); if (data->serverid) { wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - *pos++ = EAP_SAKE_AT_SERVERID; - *pos++ = 2 + data->serverid_len; - os_memcpy(pos, data->serverid, data->serverid_len); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + data->serverid, data->serverid_len); } return msg; } -static u8 * eap_sake_build_confirm(struct eap_sm *sm, - struct eap_sake_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) { - u8 *msg, *pos; + struct wpabuf *msg; + u8 *mic; wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm"); - *reqDataLen = 2 + EAP_SAKE_MIC_LEN; - msg = eap_sake_build_msg(data, &pos, id, reqDataLen, + msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (msg == NULL) { data->state = FAILURE; @@ -225,12 +214,14 @@ static u8 * eap_sake_build_confirm(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S"); - *pos++ = EAP_SAKE_AT_MIC_S; - *pos++ = 2 + EAP_SAKE_MIC_LEN; + wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S); + wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); + mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 0, - msg, *reqDataLen, pos, pos)) { + wpabuf_head(msg), wpabuf_len(msg), mic, mic)) + { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); data->state = FAILURE; os_free(msg); @@ -241,18 +232,17 @@ static u8 * eap_sake_build_confirm(struct eap_sm *sm, } -static u8 * eap_sake_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_sake_data *data = priv; switch (data->state) { case IDENTITY: - return eap_sake_build_identity(sm, data, id, reqDataLen); + return eap_sake_build_identity(sm, data, id); case CHALLENGE: - return eap_sake_build_challenge(sm, data, id, reqDataLen); + return eap_sake_build_challenge(sm, data, id); case CONFIRM: - return eap_sake_build_confirm(sm, data, id, reqDataLen); + return eap_sake_build_confirm(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq", data->state); @@ -263,21 +253,21 @@ static u8 * eap_sake_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_sake_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_sake_data *data = priv; struct eap_sake_hdr *resp; size_t len; u8 version, session_id, subtype; + const u8 *pos; - resp = (struct eap_sake_hdr *) respData; - if (respDataLen < sizeof(*resp) || - resp->type != EAP_TYPE_SAKE || - (len = ntohs(resp->length)) > respDataLen || - len < sizeof(*resp)) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame"); return TRUE; } + + resp = (struct eap_sake_hdr *) pos; version = resp->version; session_id = resp->session_id; subtype = resp->subtype; @@ -316,8 +306,8 @@ static Boolean eap_sake_check(struct eap_sm *sm, void *priv, static void eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, - u8 *respData, size_t respDataLen, - u8 *payload, size_t payloadlen) + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) { if (data->state != IDENTITY) return; @@ -330,8 +320,8 @@ static void eap_sake_process_identity(struct eap_sm *sm, static void eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, - u8 *respData, size_t respDataLen, - u8 *payload, size_t payloadlen) + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) { struct eap_sake_parse_attr attr; u8 mic_p[EAP_SAKE_MIC_LEN]; @@ -379,7 +369,8 @@ static void eap_sake_process_challenge(struct eap_sm *sm, eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 1, - respData, respDataLen, attr.mic_p, mic_p); + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p); if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); @@ -392,8 +383,8 @@ static void eap_sake_process_challenge(struct eap_sm *sm, static void eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, - u8 *respData, size_t respDataLen, - u8 *payload, size_t payloadlen) + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) { struct eap_sake_parse_attr attr; u8 mic_p[EAP_SAKE_MIC_LEN]; @@ -415,7 +406,8 @@ static void eap_sake_process_confirm(struct eap_sm *sm, eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, data->serverid, data->serverid_len, data->peerid, data->peerid_len, 1, - respData, respDataLen, attr.mic_p, mic_p); + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p); if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); @@ -426,8 +418,8 @@ static void eap_sake_process_confirm(struct eap_sm *sm, static void eap_sake_process_auth_reject(struct eap_sm *sm, struct eap_sake_data *data, - u8 *respData, size_t respDataLen, - u8 *payload, size_t payloadlen) + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject"); eap_sake_state(data, FAILURE); @@ -435,36 +427,39 @@ static void eap_sake_process_auth_reject(struct eap_sm *sm, static void eap_sake_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_sake_data *data = priv; struct eap_sake_hdr *resp; - u8 subtype, *pos, *end; + u8 subtype; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) + return; - resp = (struct eap_sake_hdr *) respData; + resp = (struct eap_sake_hdr *) pos; + end = pos + len; subtype = resp->subtype; pos = (u8 *) (resp + 1); - end = respData + ntohs(resp->length); wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", pos, end - pos); switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: - eap_sake_process_identity(sm, data, respData, respDataLen, pos, - end - pos); + eap_sake_process_identity(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: - eap_sake_process_challenge(sm, data, respData, respDataLen, - pos, end - pos); + eap_sake_process_challenge(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: - eap_sake_process_confirm(sm, data, respData, respDataLen, pos, - end - pos); + eap_sake_process_confirm(sm, data, respData, pos, end - pos); break; case EAP_SAKE_SUBTYPE_AUTH_REJECT: - eap_sake_process_auth_reject(sm, data, respData, respDataLen, - pos, end - pos); + eap_sake_process_auth_reject(sm, data, respData, pos, + end - pos); break; } } diff --git a/contrib/hostapd/eap_sim.c b/src/eap_server/eap_sim.c index 3db24923d43f4..436c65591f5d1 100644 --- a/contrib/hostapd/eap_sim.c +++ b/src/eap_server/eap_sim.c @@ -14,12 +14,10 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "crypto.h" -#include "eap_i.h" -#include "eap_sim_common.h" -#include "eap_sim_db.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_sim_common.h" +#include "eap_server/eap_sim_db.h" struct eap_sim_data { @@ -34,11 +32,15 @@ struct eap_sim_data { u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; int num_chal; - enum { START, CHALLENGE, REAUTH, SUCCESS, FAILURE } state; + enum { + START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE + } state; char *next_pseudonym; char *next_reauth_id; u16 counter; struct eap_sim_reauth *reauth; + u16 notification; + int use_result_ind; }; @@ -55,6 +57,8 @@ static const char * eap_sim_state_txt(int state) return "SUCCESS"; case FAILURE: return "FAILURE"; + case NOTIFICATION: + return "NOTIFICATION"; default: return "Unknown?!"; } @@ -79,7 +83,7 @@ static void * eap_sim_init(struct eap_sm *sm) return NULL; } - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = START; @@ -91,14 +95,14 @@ static void * eap_sim_init(struct eap_sm *sm) static void eap_sim_reset(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - free(data->next_pseudonym); - free(data->next_reauth_id); - free(data); + os_free(data->next_pseudonym); + os_free(data->next_reauth_id); + os_free(data); } -static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; u8 ver[2]; @@ -123,7 +127,7 @@ static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, ver[1] = EAP_SIM_VERSION; eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), ver, sizeof(ver)); - return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); } @@ -131,10 +135,10 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_msg *msg, u16 counter, const u8 *nonce_s) { - free(data->next_pseudonym); + os_free(data->next_pseudonym); data->next_pseudonym = eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); - free(data->next_reauth_id); + os_free(data->next_reauth_id); if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0); @@ -167,18 +171,18 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", data->next_pseudonym); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, - strlen(data->next_pseudonym), + os_strlen(data->next_pseudonym), (u8 *) data->next_pseudonym, - strlen(data->next_pseudonym)); + os_strlen(data->next_pseudonym)); } if (data->next_reauth_id) { wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", data->next_reauth_id); eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, - strlen(data->next_reauth_id), + os_strlen(data->next_reauth_id), (u8 *) data->next_reauth_id, - strlen(data->next_reauth_id)); + os_strlen(data->next_reauth_id)); } if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { @@ -191,9 +195,9 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, } -static u8 * eap_sim_build_challenge(struct eap_sm *sm, - struct eap_sim_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id) { struct eap_sim_msg *msg; @@ -209,22 +213,26 @@ static u8 * eap_sim_build_challenge(struct eap_sm *sm, return NULL; } + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt, + return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt, EAP_SIM_NONCE_MT_LEN); } -static u8 * eap_sim_build_reauth(struct eap_sm *sm, - struct eap_sim_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, + struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); - if (hostapd_get_rand(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); @@ -243,24 +251,69 @@ static u8 * eap_sim_build_reauth(struct eap_sm *sm, return NULL; } + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); } -static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, + NULL, 0); + if (data->use_result_ind) { + if (data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", + data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to " + "encrypt AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_sim_data *data = priv; switch (data->state) { case START: - return eap_sim_build_start(sm, data, id, reqDataLen); + return eap_sim_build_start(sm, data, id); case CHALLENGE: - return eap_sim_build_challenge(sm, data, id, reqDataLen); + return eap_sim_build_challenge(sm, data, id); case REAUTH: - return eap_sim_build_reauth(sm, data, id, reqDataLen); + return eap_sim_build_reauth(sm, data, id); + case NOTIFICATION: + return eap_sim_build_notification(sm, data, id); default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " "buildReq", data->state); @@ -271,20 +324,19 @@ static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_sim_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_sim_data *data = priv; - struct eap_hdr *resp; - u8 *pos, subtype; + const u8 *pos; + size_t len; + u8 subtype; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM || - (ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); + if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); return TRUE; } - subtype = pos[1]; + subtype = *pos; if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) return FALSE; @@ -311,6 +363,13 @@ static Boolean eap_sim_check(struct eap_sm *sm, void *priv, return TRUE; } break; + case NOTIFICATION: + if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; default: wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " "processing a response", data->state); @@ -329,7 +388,7 @@ static int eap_sim_supported_ver(struct eap_sim_data *data, int version) static void eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { const u8 *identity; @@ -339,11 +398,11 @@ static void eap_sim_process_start(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); if (attr->identity) { - free(sm->identity); - sm->identity = malloc(attr->identity_len); + os_free(sm->identity); + sm->identity = os_malloc(attr->identity_len); if (sm->identity) { - memcpy(sm->identity, attr->identity, - attr->identity_len); + os_memcpy(sm->identity, attr->identity, + attr->identity_len); sm->identity_len = attr->identity_len; } } @@ -370,8 +429,8 @@ static void eap_sim_process_start(struct eap_sm *sm, identity = data->reauth->identity; identity_len = data->reauth->identity_len; data->counter = data->reauth->counter; - memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); + os_memcpy(data->mk, data->reauth->mk, + EAP_SIM_MK_LEN); } } } @@ -434,7 +493,7 @@ static void eap_sim_process_start(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", sm->identity, identity_len); - memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); + os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt, attr->selected_version, ver_list, sizeof(ver_list), @@ -448,14 +507,14 @@ static void eap_sim_process_start(struct eap_sm *sm, static void eap_sim_process_challenge(struct eap_sm *sm, struct eap_sim_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { const u8 *identity; size_t identity_len; if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, + eap_sim_verify_mac(data->k_aut, respData, attr->mac, (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " @@ -466,7 +525,12 @@ static void eap_sim_process_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " "correct AT_MAC"); - eap_sim_state(data, SUCCESS); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_sim_state(data, NOTIFICATION); + } else + eap_sim_state(data, SUCCESS); identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, sm->identity_len, &identity_len); @@ -493,7 +557,7 @@ static void eap_sim_process_challenge(struct eap_sm *sm, static void eap_sim_process_reauth(struct eap_sm *sm, struct eap_sim_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; @@ -502,8 +566,8 @@ static void eap_sim_process_reauth(struct eap_sm *sm, size_t identity_len, id2_len; if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, - data->nonce_s, EAP_SIM_NONCE_S_LEN)) { + eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, + EAP_SIM_NONCE_S_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " "did not include valid AT_MAC"); goto fail; @@ -530,12 +594,17 @@ static void eap_sim_process_reauth(struct eap_sm *sm, eattr.counter, data->counter); goto fail; } - free(decrypted); + os_free(decrypted); decrypted = NULL; wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " "the correct AT_MAC"); - eap_sim_state(data, SUCCESS); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_sim_state(data, NOTIFICATION); + } else + eap_sim_state(data, SUCCESS); if (data->reauth) { identity = data->reauth->identity; @@ -573,56 +642,77 @@ fail: eap_sim_state(data, FAILURE); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; - free(decrypted); + os_free(decrypted); } static void eap_sim_process_client_error(struct eap_sm *sm, struct eap_sim_data *data, - u8 *respData, size_t respDataLen, + struct wpabuf *respData, struct eap_sim_attrs *attr) { wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", attr->client_error_code); - eap_sim_state(data, FAILURE); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_sim_state(data, SUCCESS); + else + eap_sim_state(data, FAILURE); +} + + +static void eap_sim_process_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification"); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_sim_state(data, SUCCESS); + else + eap_sim_state(data, FAILURE); } static void eap_sim_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_sim_data *data = priv; - struct eap_hdr *resp; - u8 *pos, subtype; + const u8 *pos, *end; + u8 subtype; size_t len; struct eap_sim_attrs attr; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - subtype = pos[1]; - len = ntohs(resp->length); - pos += 4; + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); + if (pos == NULL || len < 3) + return; + + end = pos + len; + subtype = *pos; + pos += 3; - if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) { + if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); eap_sim_state(data, FAILURE); return; } if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { - eap_sim_process_client_error(sm, data, respData, len, &attr); + eap_sim_process_client_error(sm, data, respData, &attr); return; } switch (data->state) { case START: - eap_sim_process_start(sm, data, respData, len, &attr); + eap_sim_process_start(sm, data, respData, &attr); break; case CHALLENGE: - eap_sim_process_challenge(sm, data, respData, len, &attr); + eap_sim_process_challenge(sm, data, respData, &attr); break; case REAUTH: - eap_sim_process_reauth(sm, data, respData, len, &attr); + eap_sim_process_reauth(sm, data, respData, &attr); + break; + case NOTIFICATION: + eap_sim_process_notification(sm, data, respData, &attr); break; default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " @@ -647,10 +737,10 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -664,10 +754,10 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_EMSK_LEN); + key = os_malloc(EAP_EMSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->emsk, EAP_EMSK_LEN); + os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } diff --git a/contrib/hostapd/eap_sim_db.c b/src/eap_server/eap_sim_db.c index 59c11ac151422..ed0bd3cd13327 100644 --- a/contrib/hostapd/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,8 +25,8 @@ #include <sys/un.h> #include "common.h" -#include "eap_sim_common.h" -#include "eap_sim_db.h" +#include "eap_common/eap_sim_common.h" +#include "eap_server/eap_sim_db.h" #include "eloop.h" struct eap_sim_pseudonym { @@ -83,7 +83,7 @@ eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, entry = data->pending; while (entry) { if (entry->aka == aka && entry->imsi_len == imsi_len && - memcmp(entry->imsi, imsi, imsi_len) == 0) { + os_memcmp(entry->imsi, imsi, imsi_len) == 0) { if (prev) prev->next = entry->next; else @@ -118,7 +118,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, Kc/SRES/RAND = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 0); + entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -126,7 +126,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, } start = buf; - if (strncmp(start, "FAILURE", 7) == 0) { + if (os_strncmp(start, "FAILURE", 7) == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " "failure"); entry->state = FAILURE; @@ -137,11 +137,11 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, num_chal = 0; while (num_chal < EAP_SIM_MAX_CHAL) { - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end) *end = '\0'; - pos = strchr(start, ':'); + pos = os_strchr(start, ':'); if (pos == NULL) goto parse_fail; *pos = '\0'; @@ -150,7 +150,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, goto parse_fail; start = pos + 1; - pos = strchr(start, ':'); + pos = os_strchr(start, ':'); if (pos == NULL) goto parse_fail; *pos = '\0'; @@ -180,7 +180,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - free(entry); + os_free(entry); } @@ -196,7 +196,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 1); + entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -204,7 +204,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, } start = buf; - if (strncmp(start, "FAILURE", 7) == 0) { + if (os_strncmp(start, "FAILURE", 7) == 0) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " "failure"); entry->state = FAILURE; @@ -213,7 +213,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, return; } - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; @@ -221,7 +221,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, goto parse_fail; start = end + 1; - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; @@ -229,7 +229,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, goto parse_fail; start = end + 1; - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; @@ -237,7 +237,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, goto parse_fail; start = end + 1; - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end == NULL) goto parse_fail; *end = '\0'; @@ -245,7 +245,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, goto parse_fail; start = end + 1; - end = strchr(start, ' '); + end = os_strchr(start, ' '); if (end) *end = '\0'; else { @@ -271,7 +271,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - free(entry); + os_free(entry); } @@ -301,21 +301,21 @@ static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) /* <cmd> <IMSI> ... */ cmd = buf; - pos = strchr(cmd, ' '); + pos = os_strchr(cmd, ' '); if (pos == NULL) goto parse_fail; *pos = '\0'; imsi = pos + 1; - pos = strchr(imsi, ' '); + pos = os_strchr(imsi, ' '); if (pos == NULL) goto parse_fail; *pos = '\0'; wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", cmd, imsi); - if (strcmp(cmd, "SIM-RESP-AUTH") == 0) + if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) eap_sim_db_sim_resp_auth(data, imsi, pos + 1); - else if (strcmp(cmd, "AKA-RESP-AUTH") == 0) + else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) eap_sim_db_aka_resp_auth(data, imsi, pos + 1); else wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " @@ -332,7 +332,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) struct sockaddr_un addr; static int counter = 0; - if (strncmp(data->fname, "unix:", 5) != 0) + if (os_strncmp(data->fname, "unix:", 5) != 0) return -1; data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); @@ -341,11 +341,11 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) return -1; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), - "/tmp/eap_sim_db_%d-%d", getpid(), counter++); - data->local_sock = strdup(addr.sun_path); + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(eap_sim_db)"); close(data->sock); @@ -353,13 +353,14 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) return -1; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", data->fname + 5); + os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("connect(eap_sim_db)"); wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", - (u8 *) addr.sun_path, strlen(addr.sun_path)); + (u8 *) addr.sun_path, + os_strlen(addr.sun_path)); close(data->sock); data->sock = -1; return -1; @@ -380,7 +381,7 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data) } if (data->local_sock) { unlink(data->local_sock); - free(data->local_sock); + os_free(data->local_sock); data->local_sock = NULL; } } @@ -399,18 +400,18 @@ void * eap_sim_db_init(const char *config, { struct eap_sim_db_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; - data->fname = strdup(config); + data->fname = os_strdup(config); if (data->fname == NULL) goto fail; - if (strncmp(data->fname, "unix:", 5) == 0) { + if (os_strncmp(data->fname, "unix:", 5) == 0) { if (eap_sim_db_open_socket(data)) goto fail; } @@ -419,25 +420,25 @@ void * eap_sim_db_init(const char *config, fail: eap_sim_db_close_socket(data); - free(data->fname); - free(data); + os_free(data->fname); + os_free(data); return NULL; } static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) { - free(p->identity); - free(p->pseudonym); - free(p); + os_free(p->identity); + os_free(p->pseudonym); + os_free(p); } static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) { - free(r->identity); - free(r->reauth_id); - free(r); + os_free(r->identity); + os_free(r->reauth_id); + os_free(r); } @@ -453,7 +454,7 @@ void eap_sim_db_deinit(void *priv) struct eap_sim_db_pending *pending, *prev_pending; eap_sim_db_close_socket(data); - free(data->fname); + os_free(data->fname); p = data->pseudonyms; while (p) { @@ -473,10 +474,10 @@ void eap_sim_db_deinit(void *priv) while (pending) { prev_pending = pending; pending = pending->next; - free(prev_pending); + os_free(prev_pending); } - free(data); + os_free(data); } @@ -581,7 +582,7 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); - free(entry); + os_free(entry); return EAP_SIM_DB_FAILURE; } @@ -597,10 +598,11 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, num_chal = entry->u.sim.num_chal; if (num_chal > max_chal) num_chal = max_chal; - memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); - memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); - memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); - free(entry); + os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); + os_memcpy(sres, entry->u.sim.sres, + num_chal * EAP_SIM_SRES_LEN); + os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); + os_free(entry); return num_chal; } @@ -609,12 +611,12 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } - len = snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); + len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); if (len < 0 || len + identity_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - memcpy(msg + len, identity, identity_len); + os_memcpy(msg + len, identity, identity_len); len += identity_len; - ret = snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); + ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return EAP_SIM_DB_FAILURE; len += ret; @@ -624,12 +626,12 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; - entry = wpa_zalloc(sizeof(*entry)); + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_get_time(&entry->timestamp); - memcpy(entry->imsi, identity, identity_len); + os_memcpy(entry->imsi, identity, identity_len); entry->imsi_len = identity_len; entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; @@ -661,20 +663,20 @@ eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, len++; } - pseudonym = malloc(len + 1); + pseudonym = os_malloc(len + 1); if (pseudonym == NULL) return NULL; - memcpy(pseudonym, identity, len); + os_memcpy(pseudonym, identity, len); pseudonym[len] = '\0'; p = data->pseudonyms; while (p) { - if (strcmp(p->pseudonym, pseudonym) == 0) + if (os_strcmp(p->pseudonym, pseudonym) == 0) break; p = p->next; } - free(pseudonym); + os_free(pseudonym); return p; } @@ -694,7 +696,7 @@ eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, p = data->pseudonyms; while (p) { if (identity_len == p->identity_len && - memcmp(p->identity, identity, identity_len) == 0) + os_memcmp(p->identity, identity, identity_len) == 0) break; p = p->next; } @@ -724,20 +726,20 @@ eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, len++; } - reauth_id = malloc(len + 1); + reauth_id = os_malloc(len + 1); if (reauth_id == NULL) return NULL; - memcpy(reauth_id, identity, len); + os_memcpy(reauth_id, identity, len); reauth_id[len] = '\0'; r = data->reauths; while (r) { - if (strcmp(r->reauth_id, reauth_id) == 0) + if (os_strcmp(r->reauth_id, reauth_id) == 0) break; r = r->next; } - free(reauth_id); + os_free(reauth_id); return r; } @@ -764,7 +766,7 @@ eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, r = data->reauths; while (r) { if (identity_len == r->identity_len && - memcmp(r->identity, identity, identity_len) == 0) + os_memcmp(r->identity, identity, identity_len) == 0) break; r = r->next; } @@ -828,9 +830,9 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) char *id, *pos, *end; u8 buf[10]; - if (hostapd_get_rand(buf, sizeof(buf))) + if (os_get_random(buf, sizeof(buf))) return NULL; - id = malloc(sizeof(buf) * 2 + 2); + id = os_malloc(sizeof(buf) * 2 + 2); if (id == NULL) return NULL; @@ -912,25 +914,25 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, if (p) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "pseudonym: %s", p->pseudonym); - free(p->pseudonym); + os_free(p->pseudonym); p->pseudonym = pseudonym; return 0; } - p = wpa_zalloc(sizeof(*p)); + p = os_zalloc(sizeof(*p)); if (p == NULL) { - free(pseudonym); + os_free(pseudonym); return -1; } p->next = data->pseudonyms; - p->identity = malloc(identity_len); + p->identity = os_malloc(identity_len); if (p->identity == NULL) { - free(p); - free(pseudonym); + os_free(p); + os_free(pseudonym); return -1; } - memcpy(p->identity, identity, identity_len); + os_memcpy(p->identity, identity, identity_len); p->identity_len = identity_len; p->pseudonym = pseudonym; data->pseudonyms = p; @@ -940,27 +942,12 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, } -/** - * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity - * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, - * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not - * free it. - * @mk: 16-byte MK from the previous full authentication - * Returns: 0 on success, -1 on failure - * - * This function adds a new re-authentication entry for an EAP-SIM user. - * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed - * anymore. - */ -int eap_sim_db_add_reauth(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter, - const u8 *mk) +static struct eap_sim_reauth * +eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len, char *reauth_id, u16 counter) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity", identity, identity_len); wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id); @@ -972,23 +959,23 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, if (r) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "reauth_id: %s", r->reauth_id); - free(r->reauth_id); + os_free(r->reauth_id); r->reauth_id = reauth_id; } else { - r = wpa_zalloc(sizeof(*r)); + r = os_zalloc(sizeof(*r)); if (r == NULL) { - free(reauth_id); - return -1; + os_free(reauth_id); + return NULL; } r->next = data->reauths; - r->identity = malloc(identity_len); + r->identity = os_malloc(identity_len); if (r->identity == NULL) { - free(r); - free(reauth_id); - return -1; + os_free(r); + os_free(reauth_id); + return NULL; } - memcpy(r->identity, identity, identity_len); + os_memcpy(r->identity, identity, identity_len); r->identity_len = identity_len; r->reauth_id = reauth_id; data->reauths = r; @@ -996,10 +983,86 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, } r->counter = counter; - memcpy(r->mk, mk, EAP_SIM_MK_LEN); + + return r; +} + + +/** + * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry + * @priv: Private data pointer from eap_sim_db_init() + * @identity: Identity of the user (may be permanent identity or pseudonym) + * @identity_len: Length of identity + * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, + * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not + * free it. + * @counter: AT_COUNTER value for fast re-authentication + * @mk: 16-byte MK from the previous full authentication or %NULL + * Returns: 0 on success, -1 on failure + * + * This function adds a new re-authentication entry for an EAP-SIM user. + * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed + * anymore. + */ +int eap_sim_db_add_reauth(void *priv, const u8 *identity, + size_t identity_len, char *reauth_id, u16 counter, + const u8 *mk) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_reauth *r; + + r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, + counter); + if (r == NULL) + return -1; + + os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); + r->aka_prime = 0; + + return 0; +} + + +#ifdef EAP_AKA_PRIME +/** + * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry + * @priv: Private data pointer from eap_sim_db_init() + * @identity: Identity of the user (may be permanent identity or pseudonym) + * @identity_len: Length of identity + * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, + * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not + * free it. + * @counter: AT_COUNTER value for fast re-authentication + * @k_encr: K_encr from the previous full authentication + * @k_aut: K_aut from the previous full authentication + * @k_re: 32-byte K_re from the previous full authentication + * Returns: 0 on success, -1 on failure + * + * This function adds a new re-authentication entry for an EAP-AKA' user. + * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed + * anymore. + */ +int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, + size_t identity_len, char *reauth_id, + u16 counter, const u8 *k_encr, const u8 *k_aut, + const u8 *k_re) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_reauth *r; + + r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, + counter); + if (r == NULL) + return -1; + + r->aka_prime = 1; + os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); + os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); return 0; } +#endif /* EAP_AKA_PRIME */ /** @@ -1036,7 +1099,6 @@ const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, * @identity: Identity of the user (may be permanent identity, pseudonym, or * reauth_id) * @identity_len: Length of identity - * @len: Buffer for length of the returned permanent identity * Returns: Pointer to the re-auth entry, or %NULL if not found */ struct eap_sim_reauth * @@ -1145,7 +1207,7 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, entry = eap_sim_db_get_pending(data, identity, identity_len, 1); if (entry) { if (entry->state == FAILURE) { - free(entry); + os_free(entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } @@ -1158,13 +1220,13 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " "received authentication data"); - memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); - memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); - memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); - memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); - memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); + os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); + os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); + os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); + os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); + os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; - free(entry); + os_free(entry); return 0; } @@ -1173,10 +1235,10 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } - len = snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); + len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); if (len < 0 || len + identity_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - memcpy(msg + len, identity, identity_len); + os_memcpy(msg + len, identity, identity_len); len += identity_len; wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " @@ -1184,13 +1246,13 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; - entry = wpa_zalloc(sizeof(*entry)); + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_get_time(&entry->timestamp); entry->aka = 1; - memcpy(entry->imsi, identity, identity_len); + os_memcpy(entry->imsi, identity, identity_len); entry->imsi_len = identity_len; entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; @@ -1247,19 +1309,19 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity, char msg[100]; int len, ret; - len = snprintf(msg, sizeof(msg), "AKA-AUTS "); + len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); if (len < 0 || len + identity_len >= sizeof(msg)) return -1; - memcpy(msg + len, identity, identity_len); + os_memcpy(msg + len, identity, identity_len); len += identity_len; - ret = snprintf(msg + len, sizeof(msg) - len, " "); + ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, auts, EAP_AKA_AUTS_LEN); - ret = snprintf(msg + len, sizeof(msg) - len, " "); + ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return -1; len += ret; diff --git a/contrib/hostapd/eap_sim_db.h b/src/eap_server/eap_sim_db.h index 6754bc3d4ccd5..66221817b2205 100644 --- a/contrib/hostapd/eap_sim_db.h +++ b/src/eap_server/eap_sim_db.h @@ -1,6 +1,6 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,7 +17,7 @@ #ifdef EAP_SIM -#include "eap_sim_common.h" +#include "eap_common/eap_sim_common.h" /* Identity prefixes */ #define EAP_SIM_PERMANENT_PREFIX '1' @@ -54,6 +54,10 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, int eap_sim_db_add_reauth(void *priv, const u8 *identity, size_t identity_len, char *reauth_id, u16 counter, const u8 *mk); +int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, + size_t identity_len, char *reauth_id, + u16 counter, const u8 *k_encr, const u8 *k_aut, + const u8 *k_re); const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, size_t identity_len, size_t *len); @@ -64,7 +68,11 @@ struct eap_sim_reauth { size_t identity_len; char *reauth_id; u16 counter; + int aka_prime; u8 mk[EAP_SIM_MK_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; }; struct eap_sim_reauth * diff --git a/contrib/hostapd/eap_tls.c b/src/eap_server/eap_tls.c index fc85337d56d91..5747940f7806d 100644 --- a/contrib/hostapd/eap_tls.c +++ b/src/eap_server/eap_tls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS (RFC 2716) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,7 +14,6 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" #include "eap_tls_common.h" @@ -27,19 +26,46 @@ static void eap_tls_reset(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; + int established; }; +static const char * eap_tls_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case CONTINUE: + return "CONTINUE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_tls_state(struct eap_tls_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s", + eap_tls_state_txt(data->state), + eap_tls_state_txt(state)); + data->state = state; +} + + static void * eap_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = START; - if (eap_tls_ssl_init(sm, &data->ssl, 1)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -54,87 +80,83 @@ static void eap_tls_reset(struct eap_sm *sm, void *priv) struct eap_tls_data *data = priv; if (data == NULL) return; - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); + eap_server_tls_ssl_deinit(sm, &data->ssl); + os_free(data); } -static u8 * eap_tls_build_start(struct eap_sm *sm, struct eap_tls_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, + struct eap_tls_data *data, u8 id) { - struct eap_hdr *req; - u8 *pos; + struct wpabuf *req; - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, + id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); - data->state = FAILURE; + eap_tls_state(data, FAILURE); return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TLS; - *pos = EAP_TLS_FLAGS_START; + wpabuf_put_u8(req, EAP_TLS_FLAGS_START); - data->state = CONTINUE; + eap_tls_state(data, CONTINUE); - return (u8 *) req; + return req; } -static u8 * eap_tls_build_req(struct eap_sm *sm, struct eap_tls_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) { - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - &req, reqDataLen); + struct eap_tls_data *data = priv; + struct wpabuf *res; - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); - data->state = SUCCESS; + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); } - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TLS, 0); - return req; -} - - -static u8 * eap_tls_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_tls_data *data = priv; + if (data->ssl.state == WAIT_FRAG_ACK) { + res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + id); + goto check_established; + } switch (data->state) { case START: - return eap_tls_build_start(sm, data, id, reqDataLen); + return eap_tls_build_start(sm, data, id); case CONTINUE: - return eap_tls_build_req(sm, data, id, reqDataLen); + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + data->established = 1; + break; default: wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", __func__, data->state); return NULL; } + + res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); + +check_established: + if (data->established && data->ssl.state != WAIT_FRAG_ACK) { + /* TLS handshake has been completed and there are no more + * fragments waiting to be sent out. */ + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + eap_tls_state(data, SUCCESS); + } + + return res; } static Boolean eap_tls_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_hdr *resp; - u8 *pos; + const u8 *pos; + size_t len; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TLS || - (ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; } @@ -143,56 +165,28 @@ static Boolean eap_tls_check(struct eap_sm *sm, void *priv, } -static void eap_tls_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) +static void eap_tls_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) { struct eap_tls_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " - "length"); - data->state = FAILURE; - return; - } - 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; - } - - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-TLS: TLS processing failed"); - data->state = FAILURE; + if (data->state == SUCCESS && wpabuf_len(data->ssl.in_buf) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " + "handshake message"); return; } + if (eap_server_tls_phase1(sm, &data->ssl) < 0) + eap_tls_state(data, FAILURE); +} - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-TLS: Locally detected fatal error " - "in TLS processing"); - data->state = FAILURE; - return; - } + +static void eap_tls_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tls_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_TLS, NULL, eap_tls_process_msg) < + 0) + eap_tls_state(data, FAILURE); } @@ -211,9 +205,9 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", @@ -234,15 +228,15 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { - emsk = malloc(EAP_EMSK_LEN); + emsk = os_malloc(EAP_EMSK_LEN); if (emsk) - memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, - EAP_EMSK_LEN); - free(eapKeyData); + os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + os_free(eapKeyData); } else emsk = NULL; diff --git a/src/eap_server/eap_tls_common.c b/src/eap_server/eap_tls_common.c new file mode 100644 index 0000000000000..befc1bf80f83d --- /dev/null +++ b/src/eap_server/eap_tls_common.c @@ -0,0 +1,410 @@ +/* + * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "sha1.h" +#include "tls.h" + + +int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + int verify_peer) +{ + data->eap = sm; + data->phase2 = sm->init_phase2; + + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + return -1; + } + + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { + wpa_printf(MSG_INFO, "SSL: Failed to configure verification " + "of TLS peer certificate"); + tls_connection_deinit(sm->ssl_ctx, data->conn); + data->conn = NULL; + return -1; + } + + /* TODO: make this configurable */ + data->tls_out_limit = 1398; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + return 0; +} + + +void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + os_free(data->in_buf); + os_free(data->out_buf); +} + + +u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == + 0) + return out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + goto fail; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) + goto fail; + + os_free(rnd); + return out; + +fail: + os_free(out); + os_free(rnd); + return NULL; +} + + +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int version, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + wpa_printf(MSG_DEBUG, "SSL: Generating Request"); + if (data->out_buf == NULL) { + wpa_printf(MSG_ERROR, "SSL: out_buf NULL in %s", __func__); + return NULL; + } + + flags = version; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->tls_out_limit) { + send_len = data->tls_out_limit - 1; + flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + plen += 4; + + req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + data->state = MSG; + } else { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + data->state = WAIT_FRAG_ACK; + } + + return req; +} + + +struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST, + id); + if (req == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK"); + wpabuf_put_u8(req, version); /* Flags */ + return req; +} + + +static int eap_server_tls_process_cont(struct eap_ssl_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_server_tls_process_fragment(struct eap_ssl_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + + /* Limit length to avoid rogue peers from causing large + * memory allocations. */ + if (message_length > 65536) { + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" + " over 64 kB)"); + return -1; + } + + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "SSL: No memory for message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) +{ + u8 *next; + size_t next_len; + + next = tls_connection_server_handshake( + sm->ssl_ctx, data->conn, + wpabuf_mhead(data->in_buf), + wpabuf_len(data->in_buf), + &next_len); + if (next == NULL) { + wpa_printf(MSG_INFO, "SSL: TLS processing failed"); + return -1; + } + if (data->out_buf) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: pending tls_out data when " + "processing new message"); + os_free(data->out_buf); + WPA_ASSERT(data->out_buf == NULL); + } + data->out_buf = wpabuf_alloc_ext_data(next, next_len); + if (data->out_buf == NULL) { + os_free(next); + return -1; + } + return 0; +} + + +static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, + const u8 **pos, size_t *left) +{ + unsigned int tls_msg_len = 0; + const u8 *end = *pos + *left; + + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (*left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + return -1; + } + tls_msg_len = WPA_GET_BE32(*pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + *pos += 4; + *left -= 4; + } + + wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " + "Message Length %u", flags, tls_msg_len); + + if (data->state == WAIT_FRAG_ACK) { + if (*left != 0) { + wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in " + "WAIT_FRAG_ACK state"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged"); + return 1; + } + + if (data->in_buf && + eap_server_tls_process_cont(data, *pos, end - *pos) < 0) + return -1; + + if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { + if (eap_server_tls_process_fragment(data, flags, tls_msg_len, + *pos, end - *pos) < 0) + return -1; + + data->state = FRAG_ACK; + return 1; + } + + if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "SSL: All fragments received"); + data->state = MSG; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&data->tmpbuf, *pos, end - *pos); + data->in_buf = &data->tmpbuf; + } + + return 0; +} + + +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) +{ + if (data->in_buf != &data->tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const u8 *plain, size_t plain_len) +{ + int res; + struct wpabuf *buf; + size_t buf_len; + + /* reserve some extra room for encryption overhead */ + buf_len = plain_len + 200; + buf = wpabuf_alloc(buf_len); + if (buf == NULL) + return NULL; + res = tls_connection_encrypt(sm->ssl_ctx, data->conn, + plain, plain_len, wpabuf_put(buf, 0), + buf_len); + if (res < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); + wpabuf_free(buf); + return NULL; + } + + wpabuf_put(buf, res); + + return buf; +} + + +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpabuf *respData, void *priv, int eap_type, + int (*proc_version)(struct eap_sm *sm, void *priv, + int peer_version), + void (*proc_msg)(struct eap_sm *sm, void *priv, + const struct wpabuf *respData)) +{ + const u8 *pos; + u8 flags; + size_t left; + int ret, res = 0; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); + if (pos == NULL || left < 1) + return 0; /* Should not happen - frame already validated */ + flags = *pos++; + left--; + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x", + (unsigned long) wpabuf_len(respData), flags); + + if (proc_version && + proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0) + return -1; + + ret = eap_server_tls_reassemble(data, flags, &pos, &left); + if (ret < 0) { + res = -1; + goto done; + } else if (ret == 1) + return 0; + + if (proc_msg) + proc_msg(sm, priv, respData); + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) { + wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in " + "TLS processing"); + res = -1; + } + +done: + eap_server_tls_free_in_buf(data); + + return res; +} diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h new file mode 100644 index 0000000000000..ce8dd252bc6dc --- /dev/null +++ b/src/eap_server/eap_tls_common.h @@ -0,0 +1,64 @@ +/* + * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +struct eap_ssl_data { + struct tls_connection *conn; + + size_t tls_out_limit; + + int phase2; + + struct eap_sm *eap; + + enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + struct wpabuf tmpbuf; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + + +int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + int verify_peer); +void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len); +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int version, u8 id); +struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data); +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const u8 *plain, size_t plain_len); +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpabuf *respData, void *priv, int eap_type, + int (*proc_version)(struct eap_sm *sm, void *priv, + int peer_version), + void (*proc_msg)(struct eap_sm *sm, void *priv, + const struct wpabuf *respData)); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/src/eap_server/eap_tnc.c b/src/eap_server/eap_tnc.c new file mode 100644 index 0000000000000..834685bb7dc27 --- /dev/null +++ b/src/eap_server/eap_tnc.c @@ -0,0 +1,536 @@ +/* + * EAP server method: EAP-TNC (Trusted Network Connect) + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "eap_i.h" +#include "tncs.h" + + +struct eap_tnc_data { + enum { START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, + FAIL } state; + enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; + struct tncs_data *tncs; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; +}; + + +/* EAP-TNC Flags */ +#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TNC_FLAGS_START 0x20 +#define EAP_TNC_VERSION_MASK 0x07 + +#define EAP_TNC_VERSION 1 + + +static void * eap_tnc_init(struct eap_sm *sm) +{ + struct eap_tnc_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + data->tncs = tncs_init(); + if (data->tncs == NULL) { + os_free(data); + return NULL; + } + + data->fragment_size = 1300; + + return data; +} + + +static void eap_tnc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + tncs_deinit(data->tncs); + os_free(data); +} + + +static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, + struct eap_tnc_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, + id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " + "request"); + data->state = FAIL; + return NULL; + } + + wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); + + data->state = CONTINUE; + + return req; +} + + +static struct wpabuf * eap_tnc_build(struct eap_sm *sm, + struct eap_tnc_data *data) +{ + struct wpabuf *req; + u8 *rpos, *rpos1; + size_t rlen; + char *start_buf, *end_buf; + size_t start_len, end_len; + size_t imv_len; + + imv_len = tncs_total_send_len(data->tncs); + + start_buf = tncs_if_tnccs_start(data->tncs); + if (start_buf == NULL) + return NULL; + start_len = os_strlen(start_buf); + end_buf = tncs_if_tnccs_end(); + if (end_buf == NULL) { + os_free(start_buf); + return NULL; + } + end_len = os_strlen(end_buf); + + rlen = start_len + imv_len + end_len; + req = wpabuf_alloc(rlen); + if (req == NULL) { + os_free(start_buf); + os_free(end_buf); + return NULL; + } + + wpabuf_put_data(req, start_buf, start_len); + os_free(start_buf); + + rpos1 = wpabuf_put(req, 0); + rpos = tncs_copy_send_buf(data->tncs, rpos1); + wpabuf_put(req, rpos - rpos1); + + wpabuf_put_data(req, end_buf, end_len); + os_free(end_buf); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", + wpabuf_head(req), wpabuf_len(req)); + + return req; +} + + +static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, + struct eap_tnc_data *data) +{ + switch (data->recommendation) { + case ALLOW: + data->state = DONE; + break; + case ISOLATE: + data->state = FAIL; + /* TODO: support assignment to a different VLAN */ + break; + case NO_ACCESS: + data->state = FAIL; + break; + case NO_RECOMMENDATION: + data->state = DONE; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); + return NULL; + } + + return eap_tnc_build(sm, data); +} + + +static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 0, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); + + return msg; +} + + +static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); + + flags = EAP_TNC_VERSION; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + plen += 4; + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + } else { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + data->state = WAIT_FRAG_ACK; + } + + return req; +} + + +static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_tnc_data *data = priv; + + switch (data->state) { + case START: + tncs_init_connection(data->tncs); + return eap_tnc_build_start(sm, data, id); + case CONTINUE: + if (data->out_buf == NULL) { + data->out_buf = eap_tnc_build(sm, data); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " + "generate message"); + return NULL; + } + data->out_used = 0; + } + return eap_tnc_build_msg(data, id); + case RECOMMENDATION: + if (data->out_buf == NULL) { + data->out_buf = eap_tnc_build_recommendation(sm, data); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " + "generate recommendation message"); + return NULL; + } + data->out_used = 0; + } + return eap_tnc_build_msg(data, id); + case WAIT_FRAG_ACK: + return eap_tnc_build_msg(data, id); + case FRAG_ACK: + return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); + case DONE: + case FAIL: + return NULL; + } + + return NULL; +} + + +static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tnc_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, + &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); + return TRUE; + } + + if (len == 0 && data->state != WAIT_FRAG_ACK) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); + return TRUE; + } + + if (len == 0) + return FALSE; /* Fragment ACK does not include flags */ + + if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", + *pos & EAP_TNC_VERSION_MASK); + return TRUE; + } + + if (*pos & EAP_TNC_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); + return TRUE; + } + + return FALSE; +} + + +static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) +{ + enum tncs_process_res res; + + res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), + wpabuf_len(inbuf)); + switch (res) { + case TNCCS_RECOMMENDATION_ALLOW: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); + data->state = RECOMMENDATION; + data->recommendation = ALLOW; + break; + case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); + data->state = RECOMMENDATION; + data->recommendation = NO_RECOMMENDATION; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); + data->state = RECOMMENDATION; + data->recommendation = ISOLATE; + break; + case TNCCS_RECOMMENDATION_NO_ACCESS: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); + data->state = RECOMMENDATION; + data->recommendation = NO_ACCESS; + break; + case TNCCS_PROCESS_ERROR: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); + data->state = FAIL; + break; + default: + break; + } +} + + +static int eap_tnc_process_cont(struct eap_tnc_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); + data->state = FAIL; + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_tnc_process_fragment(struct eap_tnc_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " + "message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static void eap_tnc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tnc_data *data = priv; + const u8 *pos, *end; + size_t len; + u8 flags; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); + if (pos == NULL) + return; /* Should not happen; message already verified */ + + end = pos + len; + + if (len == 1 && (data->state == DONE || data->state == FAIL)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " + "message"); + return; + } + + if (len == 0) { + /* fragment ack */ + flags = 0; + } else + flags = *pos++; + + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); + data->state = FAIL; + return; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + data->state = FAIL; + return; + } + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len != 0) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " + "in WAIT_FRAG_ACK state"); + data->state = FAIL; + return; + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); + data->state = CONTINUE; + return; + } + + if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { + data->state = FAIL; + return; + } + + if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { + if (eap_tnc_process_fragment(data, flags, message_length, + pos, end - pos) < 0) + data->state = FAIL; + else + data->state = FRAG_ACK; + return; + } else if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); + data->state = CONTINUE; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", + wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); + tncs_process(data, data->in_buf); + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + return data->state == DONE; +} + + +static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + return data->state == DONE; +} + + +int eap_server_tnc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); + if (eap == NULL) + return -1; + + eap->init = eap_tnc_init; + eap->reset = eap_tnc_reset; + eap->buildReq = eap_tnc_buildReq; + eap->check = eap_tnc_check; + eap->process = eap_tnc_process; + eap->isDone = eap_tnc_isDone; + eap->isSuccess = eap_tnc_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_ttls.c b/src/eap_server/eap_ttls.c index 1c0f17e764b76..b097ab2ea0b67 100644 --- a/contrib/hostapd/eap_ttls.c +++ b/src/eap_server/eap_ttls.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * hostapd / EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,23 +14,23 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" +#include "eap_server/eap_i.h" +#include "eap_server/eap_tls_common.h" #include "ms_funcs.h" -#include "md5.h" #include "sha1.h" -#include "crypto.h" +#include "eap_common/chap.h" #include "tls.h" -#include "eap_ttls.h" +#include "eap_common/eap_ttls.h" -/* Maximum supported PEAP version - * 0 = draft-ietf-pppext-eap-ttls-03.txt / draft-funk-eap-ttls-v0-00.txt +/* Maximum supported TTLS version + * 0 = RFC 5281 * 1 = draft-funk-eap-ttls-v1-00.txt */ +#ifndef EAP_TTLS_VERSION #define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ +#endif /* EAP_TTLS_VERSION */ #define MSCHAPV2_KEY_LEN 16 @@ -54,6 +54,8 @@ struct eap_ttls_data { u8 mschapv2_auth_response[20]; u8 mschapv2_ident; int tls_ia_configured; + struct wpabuf *pending_phase2_eap_resp; + int tnc_started; }; @@ -115,26 +117,26 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } -static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, - int mandatory) +static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp, + u32 avp_code, int mandatory) { - u8 *avp, *pos; + struct wpabuf *avp; + u8 *pos; - avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4); + avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4); if (avp == NULL) { - free(*resp); - *resp_len = 0; - return -1; + wpabuf_free(resp); + return NULL; } - pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len); - memcpy(pos, *resp, *resp_len); - pos += *resp_len; - AVP_PAD(avp, pos); - free(*resp); - *resp = avp; - *resp_len = pos - avp; - return 0; + pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory, + wpabuf_len(resp)); + os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp)); + pos += wpabuf_len(resp); + AVP_PAD((const u8 *) wpabuf_head(avp), pos); + wpabuf_free(resp); + wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp)); + return avp; } @@ -169,7 +171,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) pos = buf; left = len; - memset(parse, 0, sizeof(*parse)); + os_memset(parse, 0, sizeof(*parse)); while (left > 0) { u32 avp_code, avp_length, vendor_id = 0; @@ -202,7 +204,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) "underflow"); goto fail; } - vendor_id = be_to_host32(* (u32 *) dpos); + vendor_id = be_to_host32(* (be32 *) dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", (int) vendor_id); dpos += 4; @@ -214,25 +216,25 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eap == NULL) { - parse->eap = malloc(dlen); + parse->eap = os_malloc(dlen); if (parse->eap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } - memcpy(parse->eap, dpos, dlen); + os_memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; } else { - u8 *neweap = realloc(parse->eap, - parse->eap_len + dlen); + u8 *neweap = os_realloc(parse->eap, + parse->eap_len + dlen); if (neweap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } - memcpy(neweap + parse->eap_len, dpos, dlen); + os_memcpy(neweap + parse->eap_len, dpos, dlen); parse->eap = neweap; parse->eap_len += dlen; } @@ -309,7 +311,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) return 0; fail: - free(parse->eap); + os_free(parse->eap); parse->eap = NULL; return -1; } @@ -322,11 +324,11 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, u8 *challenge, *rnd; if (data->ttls_version == 0) { - return eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - len); + return eap_server_tls_derive_key(sm, &data->ssl, + "ttls challenge", len); } - memset(&keys, 0, sizeof(keys)); + os_memset(&keys, 0, sizeof(keys)); if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || keys.client_random == NULL || keys.server_random == NULL || keys.inner_secret == NULL) { @@ -336,18 +338,18 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, return NULL; } - rnd = malloc(keys.client_random_len + keys.server_random_len); - challenge = malloc(len); + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + challenge = os_malloc(len); if (rnd == NULL || challenge == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " "challenge derivation"); - free(rnd); - free(challenge); + os_free(rnd); + os_free(challenge); return NULL; } - memcpy(rnd, keys.server_random, keys.server_random_len); - memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); + os_memcpy(rnd, keys.server_random, keys.server_random_len); + os_memcpy(rnd + keys.server_random_len, keys.client_random, + keys.client_random_len); if (tls_prf(keys.inner_secret, keys.inner_secret_len, "inner application challenge", rnd, @@ -355,12 +357,12 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, challenge, len)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " "challenge"); - free(rnd); - free(challenge); + os_free(rnd); + os_free(challenge); return NULL; } - free(rnd); + os_free(rnd); wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", challenge, len); @@ -373,7 +375,7 @@ static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; @@ -398,7 +400,7 @@ static void * eap_ttls_init(struct eap_sm *sm) data->ttls_version = 0; } - if (eap_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); return NULL; @@ -415,19 +417,19 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->reset(sm, data->phase2_priv); - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); + eap_server_tls_ssl_deinit(sm, &data->ssl); + wpabuf_free(data->pending_phase2_eap_resp); + os_free(data); } -static u8 * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; +static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, + struct eap_ttls_data *data, u8 id) +{ + struct wpabuf *req; - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" " request"); @@ -435,121 +437,57 @@ static u8 * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TTLS; - *pos = EAP_TLS_FLAGS_START | data->ttls_version; + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version); eap_ttls_state(data, PHASE1); - return (u8 *) req; -} - - -static u8 * eap_ttls_build_req(struct eap_sm *sm, struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS, - data->ttls_version, id, &req, - reqDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting " - "Phase2"); - eap_ttls_state(data, PHASE2_START); - } - - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TTLS, - data->ttls_version); return req; } -static u8 * eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, - int id, u8 *plain, size_t plain_len, - size_t *out_len) -{ - int res; - u8 *pos; - struct eap_hdr *req; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TTLS; - *pos++ = data->ttls_version; - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, - pos, data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " - "data"); - free(req); - return NULL; - } - - *out_len = sizeof(struct eap_hdr) + 2 + res; - req->length = host_to_be16(*out_len); - return (u8 *) req; -} - - -static u8 * eap_ttls_build_phase2_eap_req(struct eap_sm *sm, - struct eap_ttls_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_ttls_build_phase2_eap_req( + struct eap_sm *sm, struct eap_ttls_data *data, u8 id) { - u8 *req, *encr_req; + struct wpabuf *buf, *encr_req; + u8 *req; size_t req_len; - req = data->phase2_method->buildReq(sm, data->phase2_priv, id, - &req_len); - if (req == NULL) + buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (buf == NULL) return NULL; - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encapsulate Phase 2 data", - req, req_len); + wpa_hexdump_buf_key(MSG_DEBUG, + "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf); - if (eap_ttls_avp_encapsulate(&req, &req_len, RADIUS_ATTR_EAP_MESSAGE, - 1) < 0) { + buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1); + if (buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " "packet"); return NULL; } + req = wpabuf_mhead(buf); + req_len = wpabuf_len(buf); wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " "2 data", req, req_len); - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); - free(req); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + wpabuf_free(buf); return encr_req; } -static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, - struct eap_ttls_data *data, - int id, size_t *reqDataLen) +static struct wpabuf * eap_ttls_build_phase2_mschapv2( + struct eap_sm *sm, struct eap_ttls_data *data) { - u8 *req, *encr_req, *pos, *end; + struct wpabuf *encr_req; + u8 *req, *pos, *end; int ret; size_t req_len; - pos = req = malloc(100); + pos = req = os_malloc(100); if (req == NULL) return NULL; end = req + 100; @@ -558,7 +496,7 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; - ret = snprintf((char *) pos, end - pos, "S="); + ret = os_snprintf((char *) pos, end - pos, "S="); if (ret >= 0 && ret < end - pos) pos += ret; pos += wpa_snprintf_hex_uppercase( @@ -567,7 +505,7 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, } else { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, RADIUS_VENDOR_ID_MICROSOFT, 1, 6); - memcpy(pos, "Failed", 6); + os_memcpy(pos, "Failed", 6); pos += 6; AVP_PAD(req, pos); } @@ -576,86 +514,97 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " "data", req, req_len); - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); - free(req); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + os_free(req); return encr_req; } -static u8 * eap_ttls_build_phase_finished(struct eap_sm *sm, - struct eap_ttls_data *data, - int id, int final, - size_t *reqDataLen) +static struct wpabuf * eap_ttls_build_phase_finished( + struct eap_sm *sm, struct eap_ttls_data *data, int final) { int len; - struct eap_hdr *req; - u8 *pos; + struct wpabuf *req; const int max_len = 300; - len = sizeof(struct eap_hdr) + 2 + max_len; - req = malloc(len); + req = wpabuf_alloc(max_len); if (req == NULL) return NULL; - req->code = EAP_CODE_REQUEST; - req->identifier = id; - - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TTLS; - *pos++ = data->ttls_version; - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, - final, pos, max_len); + data->ssl.conn, final, + wpabuf_mhead(req), + max_len); if (len < 0) { - free(req); + wpabuf_free(req); return NULL; } + wpabuf_put(req, len); - *reqDataLen = sizeof(struct eap_hdr) + 2 + len; - req->length = host_to_be16(*reqDataLen); - - return (u8 *) req; + return req; } -static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); + } + switch (data->state) { case START: - return eap_ttls_build_start(sm, data, id, reqDataLen); + return eap_ttls_build_start(sm, data, id); case PHASE1: - return eap_ttls_build_req(sm, data, id, reqDataLen); + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, " + "starting Phase2"); + eap_ttls_state(data, PHASE2_START); + } + break; case PHASE2_METHOD: - return eap_ttls_build_phase2_eap_req(sm, data, id, reqDataLen); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase2_eap_req(sm, data, + id); + break; case PHASE2_MSCHAPV2_RESP: - return eap_ttls_build_phase2_mschapv2(sm, data, id, - reqDataLen); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase2_mschapv2(sm, data); + break; case PHASE_FINISHED: - return eap_ttls_build_phase_finished(sm, data, id, 1, - reqDataLen); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase_finished(sm, data, 1); + break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); return NULL; } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); } static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_hdr *resp; - u8 *pos; + const u8 *pos; + size_t len; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS || - (ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); return TRUE; } @@ -674,11 +623,11 @@ static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, if (key) { buf_len = 2 + key_len; - buf = malloc(buf_len); + buf = os_malloc(buf_len); if (buf == NULL) return -1; WPA_PUT_BE16(buf, key_len); - memcpy(buf + 2, key, key_len); + os_memcpy(buf + 2, key, key_len); } else { buf = NULL; buf_len = 0; @@ -689,7 +638,7 @@ static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, data->ssl.conn, buf, buf_len); - free(buf); + os_free(buf); return ret; } @@ -700,9 +649,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, const u8 *user_password, size_t user_password_len) { - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/PAP. */ - if (!sm->user || !sm->user->password || sm->user->password_hash) { + if (!sm->user || !sm->user->password || sm->user->password_hash || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " "password configured"); eap_ttls_state(data, FAILURE); @@ -710,8 +658,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } if (sm->user->password_len != user_password_len || - memcmp(sm->user->password, user_password, user_password_len) != 0) - { + os_memcmp(sm->user->password, user_password, user_password_len) != + 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); eap_ttls_state(data, FAILURE); return; @@ -730,9 +678,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, const u8 *password, size_t password_len) { - u8 *chal, hash[MD5_MAC_LEN]; - const u8 *addr[3]; - size_t len[3]; + u8 *chal, hash[CHAP_MD5_LEN]; if (challenge == NULL || password == NULL || challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || @@ -745,9 +691,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, return; } - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/CHAP. */ - if (!sm->user || !sm->user->password || sm->user->password_hash) { + if (!sm->user || !sm->user->password || sm->user->password_hash || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " "password configured"); eap_ttls_state(data, FAILURE); @@ -763,25 +708,20 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, return; } - if (memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || + if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); - free(chal); + os_free(chal); eap_ttls_state(data, FAILURE); return; } - free(chal); + os_free(chal); /* MD5(Ident + Password + Challenge) */ - addr[0] = password; - len[0] = 1; - addr[1] = sm->user->password; - len[1] = sm->user->password_len; - addr[2] = challenge; - len[2] = challenge_len; - md5_vector(3, addr, len, hash); - - if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { + chap_md5(password[0], sm->user->password, sm->user->password_len, + challenge, challenge_len, hash); + + if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : SUCCESS); @@ -810,9 +750,8 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/MSCHAP. */ - if (!sm->user || !sm->user->password) { + if (!sm->user || !sm->user->password || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " "configured"); eap_ttls_state(data, FAILURE); @@ -828,14 +767,14 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } - if (memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || + if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); - free(chal); + os_free(chal); eap_ttls_state(data, FAILURE); return; } - free(chal); + os_free(chal); if (sm->user->password_hash) challenge_response(challenge, sm->user->password, nt_response); @@ -843,7 +782,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, nt_challenge_response(challenge, sm->user->password, sm->user->password_len, nt_response); - if (memcmp(nt_response, response + 2 + 24, 24) == 0) { + if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : SUCCESS); @@ -879,9 +818,8 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/MSCHAPV2. */ - if (!sm->user || !sm->user->password) { + if (!sm->user || !sm->user->password || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " "configured"); eap_ttls_state(data, FAILURE); @@ -910,14 +848,14 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } - if (memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || + if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); - free(chal); + os_free(chal); eap_ttls_state(data, FAILURE); return; } - free(chal); + os_free(chal); auth_challenge = challenge; peer_challenge = response + 2; @@ -943,7 +881,7 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; - if (memcmp(nt_response, rx_resp, 24) == 0) { + if (os_memcmp(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; @@ -1008,8 +946,8 @@ static int eap_ttls_phase2_eap_init(struct eap_sm *sm, data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_sm_get_eap_methods(EAP_VENDOR_IETF, - eap_type); + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); if (!data->phase2_method) return -1; @@ -1028,10 +966,11 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, struct eap_hdr *hdr; u8 *pos; size_t left; + struct wpabuf buf; const struct eap_method *m = data->phase2_method; void *priv = data->phase2_priv; - if (data->phase2_priv == NULL) { + if (priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " "initialized?!", __func__); return; @@ -1052,20 +991,35 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); - eap_ttls_phase2_eap_init(sm, data, next_type); + if (eap_ttls_phase2_eap_init(sm, data, next_type)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to " + "initialize EAP type %d", + next_type); + eap_ttls_state(data, FAILURE); + return; + } } else { eap_ttls_state(data, FAILURE); } return; } - if (m->check(sm, priv, in_data, in_len)) { + wpabuf_set(&buf, in_data, in_len); + + if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " "ignore the packet"); return; } - m->process(sm, priv, in_data, in_len); + m->process(sm, priv, &buf); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_eap_resp); + data->pending_phase2_eap_resp = wpabuf_dup(&buf); + } if (!m->isDone(sm, priv)) return; @@ -1113,7 +1067,11 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, break; } - eap_ttls_phase2_eap_init(sm, data, next_type); + if (eap_ttls_phase2_eap_init(sm, data, next_type)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize EAP " + "type %d", next_type); + eap_ttls_state(data, FAILURE); + } } @@ -1167,29 +1125,43 @@ static void eap_ttls_process_phase2_eap(struct eap_sm *sm, static void eap_ttls_process_phase2(struct eap_sm *sm, struct eap_ttls_data *data, - struct eap_hdr *resp, - u8 *in_data, size_t in_len) + struct wpabuf *in_buf) { u8 *in_decrypted; - int len_decrypted, res; + int len_decrypted; struct eap_ttls_avp parse; size_t buf_len; + u8 *in_data; + size_t in_len; + + in_data = wpabuf_mhead(in_buf); + in_len = wpabuf_len(in_buf); wpa_printf(MSG_DEBUG, "EAP-TTLS: 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) + if (data->pending_phase2_eap_resp) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response " + "- skip decryption and use old data"); + eap_ttls_process_phase2_eap( + sm, data, wpabuf_head(data->pending_phase2_eap_resp), + wpabuf_len(data->pending_phase2_eap_resp)); + wpabuf_free(data->pending_phase2_eap_resp); + data->pending_phase2_eap_resp = NULL; return; + } buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.tls_in_total; - in_decrypted = malloc(buf_len); + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf_len += 500; + buf_len *= 3; + in_decrypted = os_malloc(buf_len); if (in_decrypted == NULL) { - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " "for decryption"); return; @@ -1198,13 +1170,10 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_data, in_len, in_decrypted, buf_len); - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); - free(in_decrypted); + os_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } @@ -1222,7 +1191,7 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, eap_ttls_state(data, FAILURE); } - free(in_decrypted); + os_free(in_decrypted); return; } @@ -1231,17 +1200,17 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); - free(in_decrypted); + os_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } if (parse.user_name) { - free(sm->identity); - sm->identity = malloc(parse.user_name_len); + os_free(sm->identity); + sm->identity = os_malloc(parse.user_name_len); if (sm->identity) { - memcpy(sm->identity, parse.user_name, - parse.user_name_len); + os_memcpy(sm->identity, parse.user_name, + parse.user_name_len); sm->identity_len = parse.user_name_len; } if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) @@ -1253,6 +1222,15 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } } +#ifdef EAP_TNC + if (data->tnc_started && parse.eap == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP " + "response from peer"); + eap_ttls_state(data, FAILURE); + goto done; + } +#endif /* EAP_TNC */ + if (parse.eap) { eap_ttls_process_phase2_eap(sm, data, parse.eap, parse.eap_len); @@ -1280,29 +1258,34 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } done: - free(in_decrypted); - free(parse.eap); - } + os_free(in_decrypted); + os_free(parse.eap); +} -static void eap_ttls_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) +static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) +{ +#ifdef EAP_TNC + if (!sm->tnc || data->state != SUCCESS || data->tnc_started) + return; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC"); + if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC"); + eap_ttls_state(data, FAILURE); + return; + } + + data->tnc_started = 1; + eap_ttls_state(data, PHASE2_METHOD); +#endif /* EAP_TNC */ +} + + +static int eap_ttls_process_version(struct eap_sm *sm, void *priv, + int peer_version) { struct eap_ttls_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - int peer_version; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; if (peer_version < data->ttls_version) { wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " "use version %d", @@ -1314,49 +1297,34 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable " "TLS/IA"); - eap_ttls_state(data, FAILURE); - return; + return -1; } data->tls_ia_configured = 1; } - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " - "length"); - eap_ttls_state(data, FAILURE); - return; - } - 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; - } + return 0; +} + + +static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_ttls_data *data = priv; switch (data->state) { case PHASE1: - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing " - "failed"); + if (eap_server_tls_phase1(sm, &data->ssl) < 0) eap_ttls_state(data, FAILURE); - } break; case PHASE2_START: case PHASE2_METHOD: case PHASE_FINISHED: - eap_ttls_process_phase2(sm, data, resp, pos, left); + eap_ttls_process_phase2(sm, data, data->ssl.in_buf); + eap_ttls_start_tnc(sm, data); break; case PHASE2_MSCHAPV2_RESP: - if (data->mschapv2_resp_ok && left == 0) { + if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.in_buf) == + 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); eap_ttls_state(data, data->ttls_version > 0 ? @@ -1367,22 +1335,30 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, eap_ttls_state(data, FAILURE); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " - "frame from peer (payload len %d, expected " - "empty frame)", left); + "frame from peer (payload len %lu, " + "expected empty frame)", + (unsigned long) + wpabuf_len(data->ssl.in_buf)); eap_ttls_state(data, FAILURE); } + eap_ttls_start_tnc(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", data->state, __func__); break; } +} + - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-TTLS: Locally detected fatal error " - "in TLS processing"); +static void eap_ttls_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_ttls_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_TTLS, eap_ttls_process_version, + eap_ttls_process_msg) < 0) eap_ttls_state(data, FAILURE); - } } @@ -1399,7 +1375,7 @@ static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, struct tls_keys keys; u8 *rnd, *key; - memset(&keys, 0, sizeof(keys)); + os_memset(&keys, 0, sizeof(keys)); if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || keys.client_random == NULL || keys.server_random == NULL || keys.inner_secret == NULL) { @@ -1409,24 +1385,24 @@ static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, return NULL; } - rnd = malloc(keys.client_random_len + keys.server_random_len); - key = malloc(EAP_TLS_KEY_LEN); + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + key = os_malloc(EAP_TLS_KEY_LEN); if (rnd == NULL || key == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); - free(rnd); - free(key); + os_free(rnd); + os_free(key); return NULL; } - memcpy(rnd, keys.client_random, keys.client_random_len); - memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); if (tls_prf(keys.inner_secret, keys.inner_secret_len, "ttls v1 keying material", rnd, keys.client_random_len + keys.server_random_len, key, EAP_TLS_KEY_LEN)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - free(rnd); - free(key); + os_free(rnd); + os_free(key); return NULL; } @@ -1435,7 +1411,7 @@ static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", keys.inner_secret, keys.inner_secret_len); - free(rnd); + os_free(rnd); return key; } @@ -1450,9 +1426,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; if (data->ttls_version == 0) { - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); } else { eapKeyData = eap_ttls_v1_derive_key(sm, data); } diff --git a/contrib/hostapd/eap_vendor_test.c b/src/eap_server/eap_vendor_test.c index 4f9c14b051e62..0dd0aca911b4d 100644 --- a/contrib/hostapd/eap_vendor_test.c +++ b/src/eap_server/eap_vendor_test.c @@ -1,6 +1,6 @@ /* * hostapd / Test method for vendor specific (expanded) EAP type - * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,7 +14,6 @@ #include "includes.h" -#include "hostapd.h" #include "common.h" #include "eap_i.h" @@ -59,7 +58,7 @@ static void * eap_vendor_test_init(struct eap_sm *sm) { struct eap_vendor_test_data *data; - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = INIT; @@ -71,85 +70,56 @@ static void * eap_vendor_test_init(struct eap_sm *sm) static void eap_vendor_test_reset(struct eap_sm *sm, void *priv) { struct eap_vendor_test_data *data = priv; - free(data); + os_free(data); } -static u8 * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) +static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv, + u8 id) { struct eap_vendor_test_data *data = priv; - struct eap_hdr *req; - u8 *pos; + struct wpabuf *req; - *reqDataLen = sizeof(*req) + 8 + 1; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, + EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate " "memory for request"); return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_EXPANDED; - WPA_PUT_BE24(pos, EAP_VENDOR_ID); - pos += 3; - WPA_PUT_BE32(pos, EAP_VENDOR_TYPE); - pos += 4; - *pos = data->state == INIT ? 1 : 3; - - return (u8 *) req; + wpabuf_put_u8(req, data->state == INIT ? 1 : 3); + + return req; } static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { - struct eap_hdr *resp; - u8 *pos; + const u8 *pos; size_t len; - int vendor; - u32 method; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp)) - return TRUE; - len = ntohs(resp->length); - if (len > respDataLen) - return TRUE; - if (len < sizeof(*resp) + 8 || *pos != EAP_TYPE_EXPANDED) { + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame"); return TRUE; } - pos++; - - vendor = WPA_GET_BE24(pos); - pos += 3; - method = WPA_GET_BE32(pos); - pos++; - - if (vendor != EAP_VENDOR_ID || method != EAP_VENDOR_TYPE) - return TRUE; return FALSE; } static void eap_vendor_test_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) + struct wpabuf *respData) { struct eap_vendor_test_data *data = priv; - struct eap_hdr *resp; - u8 *pos; + const u8 *pos; + size_t len; - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos += 8; /* Skip expanded header */ + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); + if (pos == NULL || len < 1) + return; if (data->state == INIT) { if (*pos == 2) @@ -182,12 +152,12 @@ static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(key_len); + key = os_malloc(key_len); if (key == NULL) return NULL; - memset(key, 0x11, key_len / 2); - memset(key + key_len / 2, 0x22, key_len / 2); + os_memset(key, 0x11, key_len / 2); + os_memset(key + key_len / 2, 0x22, key_len / 2); *len = key_len; return key; diff --git a/src/eap_server/eap_wsc.c b/src/eap_server/eap_wsc.c new file mode 100644 index 0000000000000..3c17577889b74 --- /dev/null +++ b/src/eap_server/eap_wsc.c @@ -0,0 +1,498 @@ +/* + * EAP-WSC server for Wi-Fi Protected Setup + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "eap_i.h" +#include "eap_common/eap_wsc_common.h" +#include "wps/wps.h" + + +struct eap_wsc_data { + enum { START, MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + int registrar; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + enum wsc_op_code in_op_code, out_op_code; + size_t out_used; + size_t fragment_size; + struct wps_data *wps; + int ext_reg_timeout; +}; + + +static const char * eap_wsc_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case MSG: + return "MSG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_wsc_state(struct eap_wsc_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", + eap_wsc_state_txt(data->state), + eap_wsc_state_txt(state)); + data->state = state; +} + + +static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct eap_sm *sm = eloop_ctx; + struct eap_wsc_data *data = timeout_ctx; + + if (sm->method_pending != METHOD_PENDING_WAIT) + return; + + wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External " + "Registrar"); + data->ext_reg_timeout = 1; + eap_sm_pending_cb(sm); +} + + +static void * eap_wsc_init(struct eap_sm *sm) +{ + struct eap_wsc_data *data; + int registrar; + struct wps_config cfg; + + if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == + 0) + registrar = 0; /* Supplicant is Registrar */ + else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) + == 0) + registrar = 1; /* Supplicant is Enrollee */ + else { + wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", + sm->identity, sm->identity_len); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = registrar ? START : MSG; + data->registrar = registrar; + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = sm->wps; + cfg.registrar = registrar; + if (registrar) { + if (sm->wps == NULL || sm->wps->registrar == NULL) { + wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not " + "initialized"); + os_free(data); + return NULL; + } + } else { + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-WSC: No AP PIN (password) " + "configured for Enrollee functionality"); + os_free(data); + return NULL; + } + cfg.pin = sm->user->password; + cfg.pin_len = sm->user->password_len; + } + cfg.assoc_wps_ie = sm->assoc_wps_ie; + data->wps = wps_init(&cfg); + if (data->wps == NULL) { + os_free(data); + return NULL; + } + data->fragment_size = WSC_FRAGMENT_SIZE; + + return data; +} + + +static void eap_wsc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + wps_deinit(data->wps); + os_free(data); +} + + +static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm, + struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start"); + wpabuf_put_u8(req, WSC_Start); /* Op-Code */ + wpabuf_put_u8(req, 0); /* Flags */ + + return req; +} + + +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (2 + send_len > data->fragment_size) { + send_len = data->fragment_size - 2; + flags |= WSC_FLAGS_MF; + if (data->out_used == 0) { + flags |= WSC_FLAGS_LF; + send_len -= 2; + } + } + plen = 2 + send_len; + if (flags & WSC_FLAGS_LF) + plen += 2; + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpabuf_put_u8(req, data->out_op_code); /* Op-Code */ + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & WSC_FLAGS_LF) + wpabuf_put_be16(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + eap_wsc_state(data, MSG); + } else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_wsc_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_wsc_data *data = priv; + + switch (data->state) { + case START: + return eap_wsc_build_start(sm, data, id); + case MSG: + if (data->out_buf == NULL) { + data->out_buf = wps_get_msg(data->wps, + &data->out_op_code); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to " + "receive message from WPS"); + return NULL; + } + data->out_used = 0; + } + /* pass through */ + case WAIT_FRAG_ACK: + return eap_wsc_build_msg(data, id); + case FRAG_ACK: + return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST); + default: + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in " + "buildReq", data->state); + return NULL; + } +} + + +static Boolean eap_wsc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_wsc_process_cont(struct eap_wsc_data *data, + const u8 *buf, size_t len, u8 op_code) +{ + /* Process continuation of a pending message */ + if (op_code != data->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " + "fragment (expected %d)", + op_code, data->in_op_code); + eap_wsc_state(data, FAIL); + return -1; + } + + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + eap_wsc_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_wsc_process_fragment(struct eap_wsc_data *data, + u8 flags, u8 op_code, u16 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length " + "field in a fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " + "message"); + return -1; + } + data->in_op_code = op_code; + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in " + "first fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static void eap_wsc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_wsc_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 op_code, flags; + u16 message_length = 0; + enum wps_process_res res; + struct wpabuf tmpbuf; + + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + if (data->ext_reg_timeout) { + eap_wsc_state(data, FAIL); + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) + return; /* Should not happen; message already verified */ + + start = pos; + end = start + len; + + op_code = *pos++; + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); + return; + } + message_length = WPA_GET_BE16(pos); + pos += 2; + + if (message_length < end - pos) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " + "Length"); + return; + } + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " + "Flags 0x%x Message Length %d", + op_code, flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (op_code != WSC_FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_FRAG_ACK state", op_code); + eap_wsc_state(data, FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); + eap_wsc_state(data, MSG); + return; + } + + if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && + op_code != WSC_Done) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + eap_wsc_state(data, FAIL); + return; + } + + if (data->in_buf && + eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { + eap_wsc_state(data, FAIL); + return; + } + + if (flags & WSC_FLAGS_MF) { + if (eap_wsc_process_fragment(data, flags, op_code, + message_length, pos, end - pos) < + 0) + eap_wsc_state(data, FAIL); + else + eap_wsc_state(data, FRAG_ACK); + return; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + res = wps_process_msg(data->wps, op_code, data->in_buf); + switch (res) { + case WPS_DONE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " + "successfully - report EAP failure"); + eap_wsc_state(data, FAIL); + break; + case WPS_CONTINUE: + eap_wsc_state(data, MSG); + break; + case WPS_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); + eap_wsc_state(data, FAIL); + break; + case WPS_PENDING: + eap_wsc_state(data, MSG); + sm->method_pending = METHOD_PENDING_WAIT; + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout, + sm, data); + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + return data->state == FAIL; +} + + +static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv) +{ + /* EAP-WSC will always result in EAP-Failure */ + return FALSE; +} + + +static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) +{ + /* Recommended retransmit times: retransmit timeout 5 seconds, + * per-message timeout 15 seconds, i.e., 3 tries. */ + sm->MaxRetrans = 2; /* total 3 attempts */ + return 5; +} + + +int eap_server_wsc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + "WSC"); + if (eap == NULL) + return -1; + + eap->init = eap_wsc_init; + eap->reset = eap_wsc_reset; + eap->buildReq = eap_wsc_buildReq; + eap->check = eap_wsc_check; + eap->process = eap_wsc_process; + eap->isDone = eap_wsc_isDone; + eap->isSuccess = eap_wsc_isSuccess; + eap->getTimeout = eap_wsc_getTimeout; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c new file mode 100644 index 0000000000000..46767c5014053 --- /dev/null +++ b/src/eap_server/ikev2.c @@ -0,0 +1,1205 @@ +/* + * IKEv2 initiator (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "dh_groups.h" +#include "ikev2.h" + + +static int ikev2_process_idr(struct ikev2_initiator_data *data, + const u8 *idr, size_t idr_len); + + +void ikev2_initiator_deinit(struct ikev2_initiator_data *data) +{ + ikev2_free_keys(&data->keys); + wpabuf_free(data->r_dh_public); + wpabuf_free(data->i_dh_private); + os_free(data->IDi); + os_free(data->IDr); + os_free(data->shared_secret); + wpabuf_free(data->i_sign_msg); + wpabuf_free(data->r_sign_msg); + os_free(data->key_pad); +} + + +static int ikev2_derive_keys(struct ikev2_initiator_data *data) +{ + u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; + size_t buf_len, pad_len; + struct wpabuf *shared; + const struct ikev2_integ_alg *integ; + const struct ikev2_prf_alg *prf; + const struct ikev2_encr_alg *encr; + int ret; + const u8 *addr[2]; + size_t len[2]; + + /* RFC 4306, Sect. 2.14 */ + + integ = ikev2_get_integ(data->proposal.integ); + prf = ikev2_get_prf(data->proposal.prf); + encr = ikev2_get_encr(data->proposal.encr); + if (integ == NULL || prf == NULL || encr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); + return -1; + } + + shared = dh_derive_shared(data->r_dh_public, data->i_dh_private, + data->dh); + if (shared == NULL) + return -1; + + /* Construct Ni | Nr | SPIi | SPIr */ + + buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; + buf = os_malloc(buf_len); + if (buf == NULL) { + wpabuf_free(shared); + return -1; + } + + pos = buf; + os_memcpy(pos, data->i_nonce, data->i_nonce_len); + pos += data->i_nonce_len; + os_memcpy(pos, data->r_nonce, data->r_nonce_len); + pos += data->r_nonce_len; + os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); + pos += IKEV2_SPI_LEN; + os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); + + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + + /* Use zero-padding per RFC 4306, Sect. 2.14 */ + pad_len = data->dh->prime_len - wpabuf_len(shared); + pad = os_zalloc(pad_len ? pad_len : 1); + if (pad == NULL) { + wpabuf_free(shared); + os_free(buf); + return -1; + } + addr[0] = pad; + len[0] = pad_len; + addr[1] = wpabuf_head(shared); + len[1] = wpabuf_len(shared); + if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, + 2, addr, len, skeyseed) < 0) { + wpabuf_free(shared); + os_free(buf); + os_free(pad); + return -1; + } + os_free(pad); + wpabuf_free(shared); + + /* DH parameters are not needed anymore, so free them */ + wpabuf_free(data->r_dh_public); + data->r_dh_public = NULL; + wpabuf_free(data->i_dh_private); + data->i_dh_private = NULL; + + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", + skeyseed, prf->hash_len); + + ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, + &data->keys); + os_free(buf); + return ret; +} + + +static int ikev2_parse_transform(struct ikev2_initiator_data *data, + struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + int transform_len; + const struct ikev2_transform *t; + u16 transform_id; + const u8 *tend; + + if (end - pos < (int) sizeof(*t)) { + wpa_printf(MSG_INFO, "IKEV2: Too short transform"); + return -1; + } + + t = (const struct ikev2_transform *) pos; + transform_len = WPA_GET_BE16(t->transform_length); + if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", + transform_len); + return -1; + } + tend = pos + transform_len; + + transform_id = WPA_GET_BE16(t->transform_id); + + wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " + "Transform Type: %d Transform ID: %d", + t->type, transform_len, t->transform_type, transform_id); + + if (t->type != 0 && t->type != 3) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); + return -1; + } + + pos = (const u8 *) (t + 1); + if (pos < tend) { + wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", + pos, tend - pos); + } + + switch (t->transform_type) { + case IKEV2_TRANSFORM_ENCR: + if (ikev2_get_encr(transform_id) && + transform_id == data->proposal.encr) { + if (transform_id == ENCR_AES_CBC) { + if (tend - pos != 4) { + wpa_printf(MSG_DEBUG, "IKEV2: No " + "Transform Attr for AES"); + break; + } + if (WPA_GET_BE16(pos) != 0x800e) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } + if (WPA_GET_BE16(pos + 2) != 128) { + wpa_printf(MSG_DEBUG, "IKEV2: " + "Unsupported AES key size " + "%d bits", + WPA_GET_BE16(pos + 2)); + break; + } + } + prop->encr = transform_id; + } + break; + case IKEV2_TRANSFORM_PRF: + if (ikev2_get_prf(transform_id) && + transform_id == data->proposal.prf) + prop->prf = transform_id; + break; + case IKEV2_TRANSFORM_INTEG: + if (ikev2_get_integ(transform_id) && + transform_id == data->proposal.integ) + prop->integ = transform_id; + break; + case IKEV2_TRANSFORM_DH: + if (dh_groups_get(transform_id) && + transform_id == data->proposal.dh) + prop->dh = transform_id; + break; + } + + return transform_len; +} + + +static int ikev2_parse_proposal(struct ikev2_initiator_data *data, + struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + const u8 *pend, *ppos; + int proposal_len, i; + const struct ikev2_proposal *p; + + if (end - pos < (int) sizeof(*p)) { + wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); + return -1; + } + + p = (const struct ikev2_proposal *) pos; + proposal_len = WPA_GET_BE16(p->proposal_length); + if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", + proposal_len); + return -1; + } + wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", + p->proposal_num); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " + " Protocol ID: %d", + p->type, proposal_len, p->protocol_id); + wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", + p->spi_size, p->num_transforms); + + if (p->type != 0 && p->type != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); + return -1; + } + + if (p->protocol_id != IKEV2_PROTOCOL_IKE) { + wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " + "(only IKE allowed for EAP-IKEv2)"); + return -1; + } + + if (p->proposal_num != prop->proposal_num) { + if (p->proposal_num == prop->proposal_num + 1) + prop->proposal_num = p->proposal_num; + else { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); + return -1; + } + } + + ppos = (const u8 *) (p + 1); + pend = pos + proposal_len; + if (ppos + p->spi_size > pend) { + wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " + "in proposal"); + return -1; + } + if (p->spi_size) { + wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", + ppos, p->spi_size); + ppos += p->spi_size; + } + + /* + * For initial IKE_SA negotiation, SPI Size MUST be zero; for + * subsequent negotiations, it must be 8 for IKE. We only support + * initial case for now. + */ + if (p->spi_size != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); + return -1; + } + + if (p->num_transforms == 0) { + wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); + return -1; + } + + for (i = 0; i < (int) p->num_transforms; i++) { + int tlen = ikev2_parse_transform(data, prop, ppos, pend); + if (tlen < 0) + return -1; + ppos += tlen; + } + + if (ppos != pend) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " + "transforms"); + return -1; + } + + return proposal_len; +} + + +static int ikev2_process_sar1(struct ikev2_initiator_data *data, + const u8 *sar1, size_t sar1_len) +{ + struct ikev2_proposal_data prop; + const u8 *pos, *end; + int found = 0; + + /* Security Association Payloads: <Proposals> */ + + if (sar1 == NULL) { + wpa_printf(MSG_INFO, "IKEV2: SAr1 not received"); + return -1; + } + + os_memset(&prop, 0, sizeof(prop)); + prop.proposal_num = 1; + + pos = sar1; + end = sar1 + sar1_len; + + while (pos < end) { + int plen; + + prop.integ = -1; + prop.prf = -1; + prop.encr = -1; + prop.dh = -1; + plen = ikev2_parse_proposal(data, &prop, pos, end); + if (plen < 0) + return -1; + + if (!found && prop.integ != -1 && prop.prf != -1 && + prop.encr != -1 && prop.dh != -1) { + found = 1; + } + + pos += plen; + + /* Only one proposal expected in SAr */ + break; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal"); + return -1; + } + + if (!found) { + wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " + "INTEG:%d D-H:%d", data->proposal.proposal_num, + data->proposal.encr, data->proposal.prf, + data->proposal.integ, data->proposal.dh); + + return 0; +} + + +static int ikev2_process_ker(struct ikev2_initiator_data *data, + const u8 *ker, size_t ker_len) +{ + u16 group; + + /* + * Key Exchange Payload: + * DH Group # (16 bits) + * RESERVED (16 bits) + * Key Exchange Data (Diffie-Hellman public value) + */ + + if (ker == NULL) { + wpa_printf(MSG_INFO, "IKEV2: KEr not received"); + return -1; + } + + if (ker_len < 4 + 96) { + wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + return -1; + } + + group = WPA_GET_BE16(ker); + wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group); + + if (group != data->proposal.dh) { + wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match " + "with the selected proposal (%u)", + group, data->proposal.dh); + return -1; + } + + if (data->dh == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); + return -1; + } + + /* RFC 4306, Section 3.4: + * The length of DH public value MUST be equal to the lenght of the + * prime modulus. + */ + if (ker_len - 4 != data->dh->prime_len) { + wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " + "%ld (expected %ld)", + (long) (ker_len - 4), (long) data->dh->prime_len); + return -1; + } + + wpabuf_free(data->r_dh_public); + data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4); + if (data->r_dh_public == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value", + data->r_dh_public); + + return 0; +} + + +static int ikev2_process_nr(struct ikev2_initiator_data *data, + const u8 *nr, size_t nr_len) +{ + if (nr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Nr not received"); + return -1; + } + + if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld", + (long) nr_len); + return -1; + } + + data->r_nonce_len = nr_len; + os_memcpy(data->r_nonce, nr, nr_len); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr", + data->r_nonce, data->r_nonce_len); + + return 0; +} + + +static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + const u8 *encrypted, + size_t encrypted_len, u8 next_payload) +{ + u8 *decrypted; + size_t decrypted_len; + struct ikev2_payloads pl; + int ret = 0; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, &data->keys, 0, + hdr, encrypted, encrypted_len, + &decrypted_len); + if (decrypted == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, decrypted, + decrypted + decrypted_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (pl.idr) + ret = ikev2_process_idr(data, pl.idr, pl.idr_len); + + os_free(decrypted); + + return ret; +} + + +static int ikev2_process_sa_init(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 || + ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 || + ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0) + return -1; + + os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN); + + if (ikev2_derive_keys(data) < 0) + return -1; + + if (pl->encrypted) { + wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - " + "try to get IDr from it"); + if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted, + pl->encrypted_len, + pl->encr_next_payload) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to process " + "encrypted payload"); + return -1; + } + } + + data->state = SA_AUTH; + + return 0; +} + + +static int ikev2_process_idr(struct ikev2_initiator_data *data, + const u8 *idr, size_t idr_len) +{ + u8 id_type; + + if (idr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDr received"); + return -1; + } + + if (idr_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload"); + return -1; + } + + id_type = idr[0]; + idr += 4; + idr_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type); + wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len); + if (data->IDr) { + if (id_type != data->IDr_type || idr_len != data->IDr_len || + os_memcmp(idr, data->IDr, idr_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one " + "received earlier"); + wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d", + id_type); + wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr", + data->IDr, data->IDr_len); + return -1; + } + os_free(data->IDr); + } + data->IDr = os_malloc(idr_len); + if (data->IDr == NULL) + return -1; + os_memcpy(data->IDr, idr, idr_len); + data->IDr_len = idr_len; + data->IDr_type = id_type; + + return 0; +} + + +static int ikev2_process_cert(struct ikev2_initiator_data *data, + const u8 *cert, size_t cert_len) +{ + u8 cert_encoding; + + if (cert == NULL) { + if (data->peer_auth == PEER_AUTH_CERT) { + wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); + return -1; + } + return 0; + } + + if (cert_len < 1) { + wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); + return -1; + } + + cert_encoding = cert[0]; + cert++; + cert_len--; + + wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); + + /* TODO: validate certificate */ + + return 0; +} + + +static int ikev2_process_auth_cert(struct ikev2_initiator_data *data, + u8 method, const u8 *auth, size_t auth_len) +{ + if (method != AUTH_RSA_SIGN) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* TODO: validate AUTH */ + return 0; +} + + +static int ikev2_process_auth_secret(struct ikev2_initiator_data *data, + u8 method, const u8 *auth, + size_t auth_len) +{ + u8 auth_data[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + + if (method != AUTH_SHARED_KEY_MIC) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* msg | Ni | prf(SK_pr,IDr') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, + data->IDr, data->IDr_len, data->IDr_type, + &data->keys, 0, data->shared_secret, + data->shared_secret_len, + data->i_nonce, data->i_nonce_len, + data->key_pad, data->key_pad_len, + auth_data) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = NULL; + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + if (auth_len != prf->hash_len || + os_memcmp(auth, auth_data, auth_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); + wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", + auth, auth_len); + wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", + auth_data, prf->hash_len); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully " + "using shared keys"); + + return 0; +} + + +static int ikev2_process_auth(struct ikev2_initiator_data *data, + const u8 *auth, size_t auth_len) +{ + u8 auth_method; + + if (auth == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); + return -1; + } + + if (auth_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " + "Payload"); + return -1; + } + + auth_method = auth[0]; + auth += 4; + auth_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); + + switch (data->peer_auth) { + case PEER_AUTH_CERT: + return ikev2_process_auth_cert(data, auth_method, auth, + auth_len); + case PEER_AUTH_SECRET: + return ikev2_process_auth_secret(data, auth_method, auth, + auth_len); + } + + return -1; +} + + +static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data, + u8 next_payload, + u8 *payload, size_t payload_len) +{ + struct ikev2_payloads pl; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, payload, payload + + payload_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 || + ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || + ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) + return -1; + + return 0; +} + + +static int ikev2_process_sa_auth(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + u8 *decrypted; + size_t decrypted_len; + int ret; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, hdr, pl->encrypted, + pl->encrypted_len, &decrypted_len); + if (decrypted == NULL) + return -1; + + ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, + decrypted, decrypted_len); + os_free(decrypted); + + if (ret == 0 && !data->unknown_user) { + wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed"); + data->state = IKEV2_DONE; + } + + return ret; +} + + +static int ikev2_validate_rx_state(struct ikev2_initiator_data *data, + u8 exchange_type, u32 message_id) +{ + switch (data->state) { + case SA_INIT: + /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ], + * [SK{IDr}] */ + if (exchange_type != IKE_SA_INIT) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_INIT state", exchange_type); + return -1; + } + if (message_id != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_INIT state", message_id); + return -1; + } + break; + case SA_AUTH: + /* Expect to receive IKE_SA_AUTH: + * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH} + */ + if (exchange_type != IKE_SA_AUTH) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_AUTH state", exchange_type); + return -1; + } + if (message_id != 1) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_AUTH state", message_id); + return -1; + } + break; + case CHILD_SA: + if (exchange_type != CREATE_CHILD_SA) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in CHILD_SA state", exchange_type); + return -1; + } + if (message_id != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in CHILD_SA state", message_id); + return -1; + } + break; + case IKEV2_DONE: + return -1; + } + + return 0; +} + + +int ikev2_initiator_process(struct ikev2_initiator_data *data, + const struct wpabuf *buf) +{ + const struct ikev2_hdr *hdr; + u32 length, message_id; + const u8 *pos, *end; + struct ikev2_payloads pl; + + wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", + (unsigned long) wpabuf_len(buf)); + + if (wpabuf_len(buf) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); + return -1; + } + + hdr = (const struct ikev2_hdr *) wpabuf_head(buf); + end = wpabuf_head_u8(buf) + wpabuf_len(buf); + message_id = WPA_GET_BE32(hdr->message_id); + length = WPA_GET_BE32(hdr->length); + + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->i_spi, IKEV2_SPI_LEN); + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->r_spi, IKEV2_SPI_LEN); + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " + "Exchange Type: %u", + hdr->next_payload, hdr->version, hdr->exchange_type); + wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", + message_id, length); + + if (hdr->version != IKEV2_VERSION) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " + "(expected 0x%x)", hdr->version, IKEV2_VERSION); + return -1; + } + + if (length != wpabuf_len(buf)) { + wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " + "RX: %lu)", (unsigned long) length, + (unsigned long) wpabuf_len(buf)); + return -1; + } + + if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) + return -1; + + if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != + IKEV2_HDR_RESPONSE) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", + hdr->flags); + return -1; + } + + if (data->state != SA_INIT) { + if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Initiator's SPI"); + return -1; + } + if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Responder's SPI"); + return -1; + } + } + + pos = (const u8 *) (hdr + 1); + if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) + return -1; + + switch (data->state) { + case SA_INIT: + if (ikev2_process_sa_init(data, hdr, &pl) < 0) + return -1; + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = wpabuf_dup(buf); + break; + case SA_AUTH: + if (ikev2_process_sa_auth(data, hdr, &pl) < 0) + return -1; + break; + case CHILD_SA: + case IKEV2_DONE: + break; + } + + return 0; +} + + +static void ikev2_build_hdr(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 exchange_type, + u8 next_payload, u32 message_id) +{ + struct ikev2_hdr *hdr; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); + + /* HDR - RFC 4306, Sect. 3.1 */ + hdr = wpabuf_put(msg, sizeof(*hdr)); + os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); + os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); + hdr->next_payload = next_payload; + hdr->version = IKEV2_VERSION; + hdr->exchange_type = exchange_type; + hdr->flags = IKEV2_HDR_INITIATOR; + WPA_PUT_BE32(hdr->message_id, message_id); +} + + +static int ikev2_build_sai(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct ikev2_proposal *p; + struct ikev2_transform *t; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload"); + + /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + /* TODO: support for multiple proposals */ + p = wpabuf_put(msg, sizeof(*p)); + p->proposal_num = data->proposal.proposal_num; + p->protocol_id = IKEV2_PROTOCOL_IKE; + p->num_transforms = 4; + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + t->transform_type = IKEV2_TRANSFORM_ENCR; + WPA_PUT_BE16(t->transform_id, data->proposal.encr); + if (data->proposal.encr == ENCR_AES_CBC) { + /* Transform Attribute: Key Len = 128 bits */ + wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ + wpabuf_put_be16(msg, 128); /* 128-bit key */ + } + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; + WPA_PUT_BE16(t->transform_length, plen); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_PRF; + WPA_PUT_BE16(t->transform_id, data->proposal.prf); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_INTEG; + WPA_PUT_BE16(t->transform_id, data->proposal.integ); + + t = wpabuf_put(msg, sizeof(*t)); + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_DH; + WPA_PUT_BE16(t->transform_id, data->proposal.dh); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; + WPA_PUT_BE16(p->proposal_length, plen); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + return 0; +} + + +static int ikev2_build_kei(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct wpabuf *pv; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload"); + + data->dh = dh_groups_get(data->proposal.dh); + pv = dh_init(data->dh, &data->i_dh_private); + if (pv == NULL) { + wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); + return -1; + } + + /* KEi - RFC 4306, Sect. 3.4 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ + wpabuf_put(msg, 2); /* RESERVED */ + /* + * RFC 4306, Sect. 3.4: possible zero padding for public value to + * match the length of the prime. + */ + wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); + wpabuf_put_buf(msg, pv); + os_free(pv); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_ni(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload"); + + /* Ni - RFC 4306, Sect. 3.9 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_idi(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload"); + + if (data->IDi == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDi available"); + return -1; + } + + /* IDi - RFC 4306, Sect. 3.5 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, ID_KEY_ID); + wpabuf_put(msg, 3); /* RESERVED */ + wpabuf_put_data(msg, data->IDi, data->IDi_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_auth(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + const struct ikev2_prf_alg *prf; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + /* Authentication - RFC 4306, Sect. 3.8 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); + wpabuf_put(msg, 3); /* RESERVED */ + + /* msg | Nr | prf(SK_pi,IDi') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, + data->IDi, data->IDi_len, ID_KEY_ID, + &data->keys, 1, data->shared_secret, + data->shared_secret_len, + data->r_nonce, data->r_nonce_len, + data->key_pad, data->key_pad_len, + wpabuf_put(msg, prf->hash_len)) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = NULL; + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) +{ + struct wpabuf *msg; + + /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */ + + if (os_get_random(data->i_spi, IKEV2_SPI_LEN)) + return NULL; + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + data->i_spi, IKEV2_SPI_LEN); + + data->i_nonce_len = IKEV2_NONCE_MIN_LEN; + if (os_get_random(data->i_nonce, data->i_nonce_len)) + return NULL; + wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); + if (msg == NULL) + return NULL; + + ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); + if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || + ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) || + ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); + + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = wpabuf_dup(msg); + + return msg; +} + + +static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) +{ + struct wpabuf *msg, *plain; + const u8 *secret; + size_t secret_len; + + secret = data->get_shared_secret(data->cb_ctx, data->IDr, + data->IDr_len, &secret_len); + if (secret == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - " + "use fake value"); + /* RFC 5106, Sect. 7: + * Use a random key to fake AUTH generation in order to prevent + * probing of user identities. + */ + data->unknown_user = 1; + os_free(data->shared_secret); + data->shared_secret = os_malloc(16); + if (data->shared_secret == NULL) + return NULL; + data->shared_secret_len = 16; + if (os_get_random(data->shared_secret, 16)) + return NULL; + } else { + os_free(data->shared_secret); + data->shared_secret = os_malloc(secret_len); + if (data->shared_secret == NULL) + return NULL; + os_memcpy(data->shared_secret, secret, secret_len); + data->shared_secret_len = secret_len; + } + + /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */ + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); + if (msg == NULL) + return NULL; + ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); + + plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || + ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, + &data->keys, 1, msg, plain, + IKEV2_PAYLOAD_IDi)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); + + return msg; +} + + +struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data) +{ + switch (data->state) { + case SA_INIT: + return ikev2_build_sa_init(data); + case SA_AUTH: + return ikev2_build_sa_auth(data); + case CHILD_SA: + return NULL; + case IKEV2_DONE: + return NULL; + } + return NULL; +} diff --git a/src/eap_server/ikev2.h b/src/eap_server/ikev2.h new file mode 100644 index 0000000000000..8349fbe62de60 --- /dev/null +++ b/src/eap_server/ikev2.h @@ -0,0 +1,67 @@ +/* + * IKEv2 initiator (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IKEV2_H +#define IKEV2_H + +#include "eap_common/ikev2_common.h" + +struct ikev2_proposal_data { + u8 proposal_num; + int integ; + int prf; + int encr; + int dh; +}; + + +struct ikev2_initiator_data { + enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state; + u8 i_spi[IKEV2_SPI_LEN]; + u8 r_spi[IKEV2_SPI_LEN]; + u8 i_nonce[IKEV2_NONCE_MAX_LEN]; + size_t i_nonce_len; + u8 r_nonce[IKEV2_NONCE_MAX_LEN]; + size_t r_nonce_len; + struct wpabuf *r_dh_public; + struct wpabuf *i_dh_private; + struct ikev2_proposal_data proposal; + const struct dh_group *dh; + struct ikev2_keys keys; + u8 *IDi; + size_t IDi_len; + u8 *IDr; + size_t IDr_len; + u8 IDr_type; + struct wpabuf *r_sign_msg; + struct wpabuf *i_sign_msg; + u8 *shared_secret; + size_t shared_secret_len; + enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; + u8 *key_pad; + size_t key_pad_len; + + const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr, + size_t IDr_len, size_t *secret_len); + void *cb_ctx; + int unknown_user; +}; + + +void ikev2_initiator_deinit(struct ikev2_initiator_data *data); +int ikev2_initiator_process(struct ikev2_initiator_data *data, + const struct wpabuf *buf); +struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data); + +#endif /* IKEV2_H */ diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c new file mode 100644 index 0000000000000..21d83b3bd9614 --- /dev/null +++ b/src/eap_server/tncs.c @@ -0,0 +1,1272 @@ +/* + * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <dlfcn.h> + +#include "common.h" +#include "base64.h" +#include "tncs.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_defs.h" + + +/* TODO: TNCS must be thread-safe; review the code and add locking etc. if + * needed.. */ + +#define TNC_CONFIG_FILE "/etc/tnc_config" +#define IF_TNCCS_START \ +"<?xml version=\"1.0\"?>\n" \ +"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \ +"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \ +"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \ +"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \ +"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n" +#define IF_TNCCS_END "\n</TNCCS-Batch>" + +/* TNC IF-IMV */ + +typedef unsigned long TNC_UInt32; +typedef unsigned char *TNC_BufferReference; + +typedef TNC_UInt32 TNC_IMVID; +typedef TNC_UInt32 TNC_ConnectionID; +typedef TNC_UInt32 TNC_ConnectionState; +typedef TNC_UInt32 TNC_RetryReason; +typedef TNC_UInt32 TNC_IMV_Action_Recommendation; +typedef TNC_UInt32 TNC_IMV_Evaluation_Result; +typedef TNC_UInt32 TNC_MessageType; +typedef TNC_MessageType *TNC_MessageTypeList; +typedef TNC_UInt32 TNC_VendorID; +typedef TNC_UInt32 TNC_Subtype; +typedef TNC_UInt32 TNC_Version; +typedef TNC_UInt32 TNC_Result; +typedef TNC_UInt32 TNC_AttributeID; + +typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)( + TNC_IMVID imvID, + char *functionName, + void **pOutfunctionPointer); + +#define TNC_RESULT_SUCCESS 0 +#define TNC_RESULT_NOT_INITIALIZED 1 +#define TNC_RESULT_ALREADY_INITIALIZED 2 +#define TNC_RESULT_NO_COMMON_VERSION 3 +#define TNC_RESULT_CANT_RETRY 4 +#define TNC_RESULT_WONT_RETRY 5 +#define TNC_RESULT_INVALID_PARAMETER 6 +#define TNC_RESULT_CANT_RESPOND 7 +#define TNC_RESULT_ILLEGAL_OPERATION 8 +#define TNC_RESULT_OTHER 9 +#define TNC_RESULT_FATAL 10 + +#define TNC_CONNECTION_STATE_CREATE 0 +#define TNC_CONNECTION_STATE_HANDSHAKE 1 +#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 +#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 +#define TNC_CONNECTION_STATE_ACCESS_NONE 4 +#define TNC_CONNECTION_STATE_DELETE 5 + +#define TNC_IFIMV_VERSION_1 1 + +#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) +#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff) + +/* TNCC-TNCS Message Types */ +#define TNC_TNCCS_RECOMMENDATION 0x00000001 +#define TNC_TNCCS_ERROR 0x00000002 +#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 +#define TNC_TNCCS_REASONSTRINGS 0x00000004 + +/* Possible TNC_IMV_Action_Recommendation values: */ +enum IMV_Action_Recommendation { + TNC_IMV_ACTION_RECOMMENDATION_ALLOW, + TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS, + TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION +}; + +/* Possible TNC_IMV_Evaluation_Result values: */ +enum IMV_Evaluation_Result { + TNC_IMV_EVALUATION_RESULT_COMPLIANT, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR, + TNC_IMV_EVALUATION_RESULT_ERROR, + TNC_IMV_EVALUATION_RESULT_DONT_KNOW +}; + +struct tnc_if_imv { + struct tnc_if_imv *next; + char *name; + char *path; + void *dlhandle; /* from dlopen() */ + TNC_IMVID imvID; + TNC_MessageTypeList supported_types; + size_t num_supported_types; + + /* Functions implemented by IMVs (with TNC_IMV_ prefix) */ + TNC_Result (*Initialize)( + TNC_IMVID imvID, + TNC_Version minVersion, + TNC_Version maxVersion, + TNC_Version *pOutActualVersion); + TNC_Result (*NotifyConnectionChange)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_ConnectionState newState); + TNC_Result (*ReceiveMessage)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); + TNC_Result (*SolicitRecommendation)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID); + TNC_Result (*BatchEnding)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID); + TNC_Result (*Terminate)(TNC_IMVID imvID); + TNC_Result (*ProvideBindFunction)( + TNC_IMVID imvID, + TNC_TNCS_BindFunctionPointer bindFunction); +}; + + +#define TNC_MAX_IMV_ID 10 + +struct tncs_data { + struct tncs_data *next; + struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */ + TNC_ConnectionID connectionID; + unsigned int last_batchid; + enum IMV_Action_Recommendation recommendation; + int done; + + struct conn_imv { + u8 *imv_send; + size_t imv_send_len; + enum IMV_Action_Recommendation recommendation; + int recommendation_set; + } imv_data[TNC_MAX_IMV_ID]; + + char *tncs_message; +}; + + +struct tncs_global { + struct tnc_if_imv *imv; + TNC_ConnectionID next_conn_id; + struct tncs_data *connections; +}; + +static struct tncs_global *tncs_global_data = NULL; + + +static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID) +{ + struct tnc_if_imv *imv; + + if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL) + return NULL; + imv = tncs_global_data->imv; + while (imv) { + if (imv->imvID == imvID) + return imv; + imv = imv->next; + } + return NULL; +} + + +static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID) +{ + struct tncs_data *tncs; + + if (tncs_global_data == NULL) + return NULL; + + tncs = tncs_global_data->connections; + while (tncs) { + if (tncs->connectionID == connectionID) + return tncs; + tncs = tncs->next; + } + + wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found", + (unsigned long) connectionID); + + return NULL; +} + + +/* TNCS functions that IMVs can call */ +TNC_Result TNC_TNCS_ReportMessageTypes( + TNC_IMVID imvID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount) +{ + TNC_UInt32 i; + struct tnc_if_imv *imv; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu " + "typeCount=%lu)", + (unsigned long) imvID, (unsigned long) typeCount); + + for (i = 0; i < typeCount; i++) { + wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", + i, supportedTypes[i]); + } + + imv = tncs_get_imv(imvID); + if (imv == NULL) + return TNC_RESULT_INVALID_PARAMETER; + os_free(imv->supported_types); + imv->supported_types = + os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + if (imv->supported_types == NULL) + return TNC_RESULT_FATAL; + os_memcpy(imv->supported_types, supportedTypes, + typeCount * sizeof(TNC_MessageTypeList)); + imv->num_supported_types = typeCount; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_SendMessage( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType) +{ + struct tncs_data *tncs; + unsigned char *b64; + size_t b64len; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu " + "connectionID=%lu messageType=%lu)", + imvID, connectionID, messageType); + wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage", + message, messageLength); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs = tncs_get_conn(connectionID); + if (tncs == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + b64 = base64_encode(message, messageLength, &b64len); + if (b64 == NULL) + return TNC_RESULT_FATAL; + + os_free(tncs->imv_data[imvID].imv_send); + tncs->imv_data[imvID].imv_send_len = 0; + tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100); + if (tncs->imv_data[imvID].imv_send == NULL) { + os_free(b64); + return TNC_RESULT_OTHER; + } + + tncs->imv_data[imvID].imv_send_len = + os_snprintf((char *) tncs->imv_data[imvID].imv_send, + b64len + 100, + "<IMC-IMV-Message><Type>%08X</Type>" + "<Base64>%s</Base64></IMC-IMV-Message>", + (unsigned int) messageType, b64); + + os_free(b64); + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_RequestHandshakeRetry( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_ProvideRecommendation( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_IMV_Action_Recommendation recommendation, + TNC_IMV_Evaluation_Result evaluation) +{ + struct tncs_data *tncs; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu " + "connectionID=%lu recommendation=%lu evaluation=%lu)", + (unsigned long) imvID, (unsigned long) connectionID, + (unsigned long) recommendation, (unsigned long) evaluation); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs = tncs_get_conn(connectionID); + if (tncs == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs->imv_data[imvID].recommendation = recommendation; + tncs->imv_data[imvID].recommendation_set = 1; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_GetAttribute( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_AttributeID attribureID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer, + TNC_UInt32 *pOutValueLength) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_SetAttribute( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_AttributeID attribureID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_BindFunction( + TNC_IMVID imvID, + char *functionName, + void **pOutFunctionPointer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, " + "functionName='%s')", (unsigned long) imvID, functionName); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (pOutFunctionPointer == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0) + *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes; + else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0) + *pOutFunctionPointer = TNC_TNCS_SendMessage; + else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") == + 0) + *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry; + else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") == + 0) + *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation; + else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0) + *pOutFunctionPointer = TNC_TNCS_GetAttribute; + else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0) + *pOutFunctionPointer = TNC_TNCS_SetAttribute; + else + *pOutFunctionPointer = NULL; + + return TNC_RESULT_SUCCESS; +} + + +static void * tncs_get_sym(void *handle, char *func) +{ + void *fptr; + + fptr = dlsym(handle, func); + + return fptr; +} + + +static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv) +{ + void *handle = imv->dlhandle; + + /* Mandatory IMV functions */ + imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize"); + if (imv->Initialize == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_Initialize"); + return -1; + } + + imv->SolicitRecommendation = tncs_get_sym( + handle, "TNC_IMV_SolicitRecommendation"); + if (imv->SolicitRecommendation == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_SolicitRecommendation"); + return -1; + } + + imv->ProvideBindFunction = + tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction"); + if (imv->ProvideBindFunction == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_ProvideBindFunction"); + return -1; + } + + /* Optional IMV functions */ + imv->NotifyConnectionChange = + tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange"); + imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage"); + imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding"); + imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate"); + + return 0; +} + + +static int tncs_imv_initialize(struct tnc_if_imv *imv) +{ + TNC_Result res; + TNC_Version imv_ver; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'", + imv->name); + res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1, + TNC_IFIMV_VERSION_1, &imv_ver); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu", + (unsigned long) res, (unsigned long) imv_ver); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_terminate(struct tnc_if_imv *imv) +{ + TNC_Result res; + + if (imv->Terminate == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'", + imv->name); + res = imv->Terminate(imv->imvID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for " + "IMV '%s'", imv->name); + res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv, + TNC_ConnectionID conn, + TNC_ConnectionState state) +{ + TNC_Result res; + + if (imv->NotifyConnectionChange == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)" + " for IMV '%s'", (int) state, imv->name); + res = imv->NotifyConnectionChange(imv->imvID, conn, state); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_load_imv(struct tnc_if_imv *imv) +{ + if (imv->path == NULL) { + wpa_printf(MSG_DEBUG, "TNC: No IMV configured"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)", + imv->name, imv->path); + imv->dlhandle = dlopen(imv->path, RTLD_LAZY); + if (imv->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s", + imv->name, imv->path, dlerror()); + return -1; + } + + if (tncs_imv_resolve_funcs(imv) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions"); + return -1; + } + + if (tncs_imv_initialize(imv) < 0 || + tncs_imv_provide_bind_function(imv) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV"); + return -1; + } + + return 0; +} + + +static void tncs_free_imv(struct tnc_if_imv *imv) +{ + os_free(imv->name); + os_free(imv->path); + os_free(imv->supported_types); +} + +static void tncs_unload_imv(struct tnc_if_imv *imv) +{ + tncs_imv_terminate(imv); + + if (imv->dlhandle) + dlclose(imv->dlhandle); + + tncs_free_imv(imv); +} + + +static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type) +{ + size_t i; + unsigned int vendor, subtype; + + if (imv == NULL || imv->supported_types == NULL) + return 0; + + vendor = type >> 8; + subtype = type & 0xff; + + for (i = 0; i < imv->num_supported_types; i++) { + unsigned int svendor, ssubtype; + svendor = imv->supported_types[i] >> 8; + ssubtype = imv->supported_types[i] & 0xff; + if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && + (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) + return 1; + } + + return 0; +} + + +static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type, + const u8 *msg, size_t len) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len); + + for (imv = tncs->imv; imv; imv = imv->next) { + if (imv->ReceiveMessage == NULL || + !tncs_supported_type(imv, type)) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'", + imv->name); + res = imv->ReceiveMessage(imv->imvID, tncs->connectionID, + (TNC_BufferReference) msg, len, + type); + wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", + (unsigned long) res); + } +} + + +static void tncs_batch_ending(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + for (imv = tncs->imv; imv; imv = imv->next) { + if (imv->BatchEnding == NULL) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'", + imv->name); + res = imv->BatchEnding(imv->imvID, tncs->connectionID); + wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu", + (unsigned long) res); + } +} + + +static void tncs_solicit_recommendation(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + for (imv = tncs->imv; imv; imv = imv->next) { + if (tncs->imv_data[imv->imvID].recommendation_set) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for " + "IMV '%s'", imv->name); + res = imv->SolicitRecommendation(imv->imvID, + tncs->connectionID); + wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu", + (unsigned long) res); + } +} + + +void tncs_init_connection(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + int i; + + for (imv = tncs->imv; imv; imv = imv->next) { + tncs_imv_notify_connection_change( + imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE); + tncs_imv_notify_connection_change( + imv, tncs->connectionID, + TNC_CONNECTION_STATE_HANDSHAKE); + } + + for (i = 0; i < TNC_MAX_IMV_ID; i++) { + os_free(tncs->imv_data[i].imv_send); + tncs->imv_data[i].imv_send = NULL; + tncs->imv_data[i].imv_send_len = 0; + } +} + + +size_t tncs_total_send_len(struct tncs_data *tncs) +{ + int i; + size_t len = 0; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) + len += tncs->imv_data[i].imv_send_len; + if (tncs->tncs_message) + len += os_strlen(tncs->tncs_message); + return len; +} + + +u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos) +{ + int i; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) { + if (tncs->imv_data[i].imv_send == NULL) + continue; + + os_memcpy(pos, tncs->imv_data[i].imv_send, + tncs->imv_data[i].imv_send_len); + pos += tncs->imv_data[i].imv_send_len; + os_free(tncs->imv_data[i].imv_send); + tncs->imv_data[i].imv_send = NULL; + tncs->imv_data[i].imv_send_len = 0; + } + + if (tncs->tncs_message) { + size_t len = os_strlen(tncs->tncs_message); + os_memcpy(pos, tncs->tncs_message, len); + pos += len; + os_free(tncs->tncs_message); + tncs->tncs_message = NULL; + } + + return pos; +} + + +char * tncs_if_tnccs_start(struct tncs_data *tncs) +{ + char *buf = os_malloc(1000); + if (buf == NULL) + return NULL; + tncs->last_batchid++; + os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid); + return buf; +} + + +char * tncs_if_tnccs_end(void) +{ + char *buf = os_malloc(100); + if (buf == NULL) + return NULL; + os_snprintf(buf, 100, IF_TNCCS_END); + return buf; +} + + +static int tncs_get_type(char *start, unsigned int *type) +{ + char *pos = os_strstr(start, "<Type>"); + if (pos == NULL) + return -1; + pos += 6; + *type = strtoul(pos, NULL, 16); + return 0; +} + + +static unsigned char * tncs_get_base64(char *start, size_t *decoded_len) +{ + char *pos, *pos2; + unsigned char *decoded; + + pos = os_strstr(start, "<Base64>"); + if (pos == NULL) + return NULL; + + pos += 8; + pos2 = os_strstr(pos, "</Base64>"); + if (pos2 == NULL) + return NULL; + *pos2 = '\0'; + + decoded = base64_decode((unsigned char *) pos, os_strlen(pos), + decoded_len); + *pos2 = '<'; + if (decoded == NULL) { + wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); + } + + return decoded; +} + + +static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs) +{ + enum IMV_Action_Recommendation rec; + struct tnc_if_imv *imv; + TNC_ConnectionState state; + char *txt; + + wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs"); + + if (tncs->done) + return TNCCS_PROCESS_OK_NO_RECOMMENDATION; + + tncs_solicit_recommendation(tncs); + + /* Select the most restrictive recommendation */ + rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION; + for (imv = tncs->imv; imv; imv = imv->next) { + TNC_IMV_Action_Recommendation irec; + irec = tncs->imv_data[imv->imvID].recommendation; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) + rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE && + rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) + rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW && + rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION) + rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW; + } + + wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec); + tncs->recommendation = rec; + tncs->done = 1; + + txt = NULL; + switch (rec) { + case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: + case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: + txt = "allow"; + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: + txt = "isolate"; + state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; + break; + case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: + txt = "none"; + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + default: + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + } + + if (txt) { + os_free(tncs->tncs_message); + tncs->tncs_message = os_zalloc(200); + if (tncs->tncs_message) { + os_snprintf(tncs->tncs_message, 199, + "<TNCC-TNCS-Message><Type>%08X</Type>" + "<XML><TNCCS-Recommendation type=\"%s\">" + "</TNCCS-Recommendation></XML>" + "</TNCC-TNCS-Message>", + TNC_TNCCS_RECOMMENDATION, txt); + } + } + + for (imv = tncs->imv; imv; imv = imv->next) { + tncs_imv_notify_connection_change(imv, tncs->connectionID, + state); + } + + switch (rec) { + case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: + return TNCCS_RECOMMENDATION_ALLOW; + case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: + return TNCCS_RECOMMENDATION_NO_ACCESS; + case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: + return TNCCS_RECOMMENDATION_ISOLATE; + case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: + return TNCCS_RECOMMENDATION_NO_RECOMMENDATION; + default: + return TNCCS_PROCESS_ERROR; + } +} + + +enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, + const u8 *msg, size_t len) +{ + char *buf, *start, *end, *pos, *pos2, *payload; + unsigned int batch_id; + unsigned char *decoded; + size_t decoded_len; + + buf = os_malloc(len + 1); + if (buf == NULL) + return TNCCS_PROCESS_ERROR; + + os_memcpy(buf, msg, len); + buf[len] = '\0'; + start = os_strstr(buf, "<TNCCS-Batch "); + end = os_strstr(buf, "</TNCCS-Batch>"); + if (start == NULL || end == NULL || start > end) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + start += 13; + while (*start == ' ') + start++; + *end = '\0'; + + pos = os_strstr(start, "BatchId="); + if (pos == NULL) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + pos += 8; + if (*pos == '"') + pos++; + batch_id = atoi(pos); + wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", + batch_id); + if (batch_id != tncs->last_batchid + 1) { + wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " + "%u (expected %u)", + batch_id, tncs->last_batchid + 1); + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + tncs->last_batchid = batch_id; + + while (*pos != '\0' && *pos != '>') + pos++; + if (*pos == '\0') { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + pos++; + payload = start; + + /* + * <IMC-IMV-Message> + * <Type>01234567</Type> + * <Base64>foo==</Base64> + * </IMC-IMV-Message> + */ + + while (*start) { + char *endpos; + unsigned int type; + + pos = os_strstr(start, "<IMC-IMV-Message>"); + if (pos == NULL) + break; + start = pos + 17; + end = os_strstr(start, "</IMC-IMV-Message>"); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 18; + + if (tncs_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); + + decoded = tncs_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + + tncs_send_to_imvs(tncs, type, decoded, decoded_len); + + os_free(decoded); + + start = end; + } + + /* + * <TNCC-TNCS-Message> + * <Type>01234567</Type> + * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML> + * <Base64>foo==</Base64> + * </TNCC-TNCS-Message> + */ + + start = payload; + while (*start) { + unsigned int type; + char *xml, *xmlend, *endpos; + + pos = os_strstr(start, "<TNCC-TNCS-Message>"); + if (pos == NULL) + break; + start = pos + 19; + end = os_strstr(start, "</TNCC-TNCS-Message>"); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 20; + + if (tncs_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", + type); + + /* Base64 OR XML */ + decoded = NULL; + xml = NULL; + xmlend = NULL; + pos = os_strstr(start, "<XML>"); + if (pos) { + pos += 5; + pos2 = os_strstr(pos, "</XML>"); + if (pos2 == NULL) { + *endpos = '<'; + start = end; + continue; + } + xmlend = pos2; + xml = pos; + } else { + decoded = tncs_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + } + + if (decoded) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message Base64", + decoded, decoded_len); + os_free(decoded); + } + + if (xml) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message XML", + (unsigned char *) xml, + xmlend - xml); + } + + start = end; + } + + os_free(buf); + + tncs_batch_ending(tncs); + + if (tncs_total_send_len(tncs) == 0) + return tncs_derive_recommendation(tncs); + + return TNCCS_PROCESS_OK_NO_RECOMMENDATION; +} + + +static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end, + int *error) +{ + struct tnc_if_imv *imv; + char *pos, *pos2; + + if (id >= TNC_MAX_IMV_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMVs"); + return NULL; + } + + imv = os_zalloc(sizeof(*imv)); + if (imv == NULL) { + *error = 1; + return NULL; + } + + imv->imvID = id; + + pos = start; + wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos); + if (pos + 1 >= end || *pos != '"') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no starting quotation mark)", start); + os_free(imv); + return NULL; + } + + pos++; + pos2 = pos; + while (pos2 < end && *pos2 != '"') + pos2++; + if (pos2 >= end) { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no ending quotation mark)", start); + os_free(imv); + return NULL; + } + *pos2 = '\0'; + wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); + imv->name = os_strdup(pos); + + pos = pos2 + 1; + if (pos >= end || *pos != ' ') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no space after name)", start); + os_free(imv); + return NULL; + } + + pos++; + wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos); + imv->path = os_strdup(pos); + + return imv; +} + + +static int tncs_read_config(struct tncs_global *global) +{ + char *config, *end, *pos, *line_end; + size_t config_len; + struct tnc_if_imv *imv, *last; + int id = 0; + + last = NULL; + + config = os_readfile(TNC_CONFIG_FILE, &config_len); + if (config == NULL) { + wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " + "file '%s'", TNC_CONFIG_FILE); + return -1; + } + + end = config + config_len; + for (pos = config; pos < end; pos = line_end + 1) { + line_end = pos; + while (*line_end != '\n' && *line_end != '\r' && + line_end < end) + line_end++; + *line_end = '\0'; + + if (os_strncmp(pos, "IMV ", 4) == 0) { + int error = 0; + + imv = tncs_parse_imv(id++, pos + 4, line_end, &error); + if (error) + return -1; + if (imv) { + if (last == NULL) + global->imv = imv; + else + last->next = imv; + last = imv; + } + } + } + + os_free(config); + + return 0; +} + + +struct tncs_data * tncs_init(void) +{ + struct tncs_data *tncs; + + if (tncs_global_data == NULL) + return NULL; + + tncs = os_zalloc(sizeof(*tncs)); + if (tncs == NULL) + return NULL; + tncs->imv = tncs_global_data->imv; + tncs->connectionID = tncs_global_data->next_conn_id++; + tncs->next = tncs_global_data->connections; + tncs_global_data->connections = tncs; + + return tncs; +} + + +void tncs_deinit(struct tncs_data *tncs) +{ + int i; + struct tncs_data *prev, *conn; + + if (tncs == NULL) + return; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) + os_free(tncs->imv_data[i].imv_send); + + prev = NULL; + conn = tncs_global_data->connections; + while (conn) { + if (conn == tncs) { + if (prev) + prev->next = tncs->next; + else + tncs_global_data->connections = tncs->next; + break; + } + prev = conn; + conn = conn->next; + } + + os_free(tncs->tncs_message); + os_free(tncs); +} + + +int tncs_global_init(void) +{ + struct tnc_if_imv *imv; + + tncs_global_data = os_zalloc(sizeof(*tncs_global_data)); + if (tncs_global_data == NULL) + return -1; + + if (tncs_read_config(tncs_global_data) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); + goto failed; + } + + for (imv = tncs_global_data->imv; imv; imv = imv->next) { + if (tncs_load_imv(imv)) { + wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'", + imv->name); + goto failed; + } + } + + return 0; + +failed: + tncs_global_deinit(); + return -1; +} + + +void tncs_global_deinit(void) +{ + struct tnc_if_imv *imv, *prev; + + if (tncs_global_data == NULL) + return; + + imv = tncs_global_data->imv; + while (imv) { + tncs_unload_imv(imv); + + prev = imv; + imv = imv->next; + os_free(prev); + } + + os_free(tncs_global_data); +} + + +struct wpabuf * tncs_build_soh_request(void) +{ + struct wpabuf *buf; + + /* + * Build a SoH Request TLV (to be used inside SoH EAP Extensions + * Method) + */ + + buf = wpabuf_alloc(8 + 4); + if (buf == NULL) + return NULL; + + /* Vendor-Specific TLV (Microsoft) - SoH Request */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ + wpabuf_put_be16(buf, 8); /* Length */ + + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ + + wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */ + wpabuf_put_be16(buf, 0); /* Length */ + + return buf; +} + + +struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, + int *failure) +{ + wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len); + *failure = 0; + + /* TODO: return MS-SoH Response TLV */ + + return NULL; +} diff --git a/src/eap_server/tncs.h b/src/eap_server/tncs.h new file mode 100644 index 0000000000000..18a3a1fa3c474 --- /dev/null +++ b/src/eap_server/tncs.h @@ -0,0 +1,49 @@ +/* + * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TNCS_H +#define TNCS_H + +struct tncs_data; + +struct tncs_data * tncs_init(void); +void tncs_deinit(struct tncs_data *tncs); +void tncs_init_connection(struct tncs_data *tncs); +size_t tncs_total_send_len(struct tncs_data *tncs); +u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos); +char * tncs_if_tnccs_start(struct tncs_data *tncs); +char * tncs_if_tnccs_end(void); + +enum tncs_process_res { + TNCCS_PROCESS_ERROR = -1, + TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, + TNCCS_RECOMMENDATION_ERROR, + TNCCS_RECOMMENDATION_ALLOW, + TNCCS_RECOMMENDATION_NONE, + TNCCS_RECOMMENDATION_ISOLATE, + TNCCS_RECOMMENDATION_NO_ACCESS, + TNCCS_RECOMMENDATION_NO_RECOMMENDATION +}; + +enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, + const u8 *msg, size_t len); + +int tncs_global_init(void); +void tncs_global_deinit(void); + +struct wpabuf * tncs_build_soh_request(void); +struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, + int *failure); + +#endif /* TNCS_H */ diff --git a/src/eapol_supp/.gitignore b/src/eapol_supp/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/eapol_supp/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/eapol_supp/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c new file mode 100644 index 0000000000000..c832b5ae2cbf6 --- /dev/null +++ b/src/eapol_supp/eapol_supp_sm.c @@ -0,0 +1,1871 @@ +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp_sm.h" +#include "eap_peer/eap.h" +#include "eloop.h" +#include "eapol_common.h" +#include "md5.h" +#include "rc4.h" +#include "state_machine.h" +#include "wpabuf.h" + +#define STATE_MACHINE_DATA struct eapol_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAPOL" + + +/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ + +/** + * struct eapol_sm - Internal data for EAPOL state machines + */ +struct eapol_sm { + /* Timers */ + unsigned int authWhile; + unsigned int heldWhile; + unsigned int startWhen; + unsigned int idleWhile; /* for EAP state machine */ + int timer_tick_enabled; + + /* Global variables */ + Boolean eapFail; + Boolean eapolEap; + Boolean eapSuccess; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + PortControl portControl; + Boolean portEnabled; + PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ + Boolean portValid; + Boolean suppAbort; + Boolean suppFail; + Boolean suppStart; + Boolean suppSuccess; + Boolean suppTimeout; + + /* Supplicant PAE state machine */ + enum { + SUPP_PAE_UNKNOWN = 0, + SUPP_PAE_DISCONNECTED = 1, + SUPP_PAE_LOGOFF = 2, + SUPP_PAE_CONNECTING = 3, + SUPP_PAE_AUTHENTICATING = 4, + SUPP_PAE_AUTHENTICATED = 5, + /* unused(6) */ + SUPP_PAE_HELD = 7, + SUPP_PAE_RESTART = 8, + SUPP_PAE_S_FORCE_AUTH = 9, + SUPP_PAE_S_FORCE_UNAUTH = 10 + } SUPP_PAE_state; /* dot1xSuppPaeState */ + /* Variables */ + Boolean userLogoff; + Boolean logoffSent; + unsigned int startCount; + Boolean eapRestart; + PortControl sPortMode; + /* Constants */ + unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ + unsigned int startPeriod; /* dot1xSuppStartPeriod */ + unsigned int maxStart; /* dot1xSuppMaxStart */ + + /* Key Receive state machine */ + enum { + KEY_RX_UNKNOWN = 0, + KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE + } KEY_RX_state; + /* Variables */ + Boolean rxKey; + + /* Supplicant Backend state machine */ + enum { + SUPP_BE_UNKNOWN = 0, + SUPP_BE_INITIALIZE = 1, + SUPP_BE_IDLE = 2, + SUPP_BE_REQUEST = 3, + SUPP_BE_RECEIVE = 4, + SUPP_BE_RESPONSE = 5, + SUPP_BE_FAIL = 6, + SUPP_BE_TIMEOUT = 7, + SUPP_BE_SUCCESS = 8 + } SUPP_BE_state; /* dot1xSuppBackendPaeState */ + /* Variables */ + Boolean eapNoResp; + Boolean eapReq; + Boolean eapResp; + /* Constants */ + unsigned int authPeriod; /* dot1xSuppAuthPeriod */ + + /* Statistics */ + unsigned int dot1xSuppEapolFramesRx; + unsigned int dot1xSuppEapolFramesTx; + unsigned int dot1xSuppEapolStartFramesTx; + unsigned int dot1xSuppEapolLogoffFramesTx; + unsigned int dot1xSuppEapolRespFramesTx; + unsigned int dot1xSuppEapolReqIdFramesRx; + unsigned int dot1xSuppEapolReqFramesRx; + unsigned int dot1xSuppInvalidEapolFramesRx; + unsigned int dot1xSuppEapLengthErrorFramesRx; + unsigned int dot1xSuppLastEapolFrameVersion; + unsigned char dot1xSuppLastEapolFrameSource[6]; + + /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ + Boolean changed; + struct eap_sm *eap; + struct eap_peer_config *config; + Boolean initial_req; + u8 *last_rx_key; + size_t last_rx_key_len; + struct wpabuf *eapReqData; /* for EAP */ + Boolean altAccept; /* for EAP */ + Boolean altReject; /* for EAP */ + Boolean replay_counter_valid; + u8 last_replay_counter[16]; + struct eapol_config conf; + struct eapol_ctx *ctx; + enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } + cb_status; + Boolean cached_pmk; + + Boolean unicast_key_received, broadcast_key_received; +}; + + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +static void eapol_sm_txLogoff(struct eapol_sm *sm); +static void eapol_sm_txStart(struct eapol_sm *sm); +static void eapol_sm_processKey(struct eapol_sm *sm); +static void eapol_sm_getSuppRsp(struct eapol_sm *sm); +static void eapol_sm_txSuppRsp(struct eapol_sm *sm); +static void eapol_sm_abortSupp(struct eapol_sm *sm); +static void eapol_sm_abort_cached(struct eapol_sm *sm); +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); + + +/* Port Timers state machine - implemented as a function that will be called + * once a second as a registered event loop timeout */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_sm *sm = timeout_ctx; + + if (sm->authWhile > 0) { + sm->authWhile--; + if (sm->authWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); + } + if (sm->heldWhile > 0) { + sm->heldWhile--; + if (sm->heldWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); + } + if (sm->startWhen > 0) { + sm->startWhen--; + if (sm->startWhen == 0) + wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); + } + if (sm->idleWhile > 0) { + sm->idleWhile--; + if (sm->idleWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); + } + + if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, + sm); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); + sm->timer_tick_enabled = 0; + } + eapol_sm_step(sm); +} + + +static void eapol_enable_timer_tick(struct eapol_sm *sm) +{ + if (sm->timer_tick_enabled) + return; + wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); + sm->timer_tick_enabled = 1; + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +SM_STATE(SUPP_PAE, LOGOFF) +{ + SM_ENTRY(SUPP_PAE, LOGOFF); + eapol_sm_txLogoff(sm); + sm->logoffSent = TRUE; + sm->suppPortStatus = Unauthorized; +} + + +SM_STATE(SUPP_PAE, DISCONNECTED) +{ + SM_ENTRY(SUPP_PAE, DISCONNECTED); + sm->sPortMode = Auto; + sm->startCount = 0; + sm->logoffSent = FALSE; + sm->suppPortStatus = Unauthorized; + sm->suppAbort = TRUE; + + sm->unicast_key_received = FALSE; + sm->broadcast_key_received = FALSE; +} + + +SM_STATE(SUPP_PAE, CONNECTING) +{ + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; + SM_ENTRY(SUPP_PAE, CONNECTING); + if (send_start) { + sm->startWhen = sm->startPeriod; + sm->startCount++; + } else { + /* + * Do not send EAPOL-Start immediately since in most cases, + * Authenticator is going to start authentication immediately + * after association and an extra EAPOL-Start is just going to + * delay authentication. Use a short timeout to send the first + * EAPOL-Start if Authenticator does not start authentication. + */ + sm->startWhen = 3; + } + eapol_enable_timer_tick(sm); + sm->eapolEap = FALSE; + if (send_start) + eapol_sm_txStart(sm); +} + + +SM_STATE(SUPP_PAE, AUTHENTICATING) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATING); + sm->startCount = 0; + sm->suppSuccess = FALSE; + sm->suppFail = FALSE; + sm->suppTimeout = FALSE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; + sm->suppStart = TRUE; +} + + +SM_STATE(SUPP_PAE, HELD) +{ + SM_ENTRY(SUPP_PAE, HELD); + sm->heldWhile = sm->heldPeriod; + eapol_enable_timer_tick(sm); + sm->suppPortStatus = Unauthorized; + sm->cb_status = EAPOL_CB_FAILURE; +} + + +SM_STATE(SUPP_PAE, AUTHENTICATED) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATED); + sm->suppPortStatus = Authorized; + sm->cb_status = EAPOL_CB_SUCCESS; +} + + +SM_STATE(SUPP_PAE, RESTART) +{ + SM_ENTRY(SUPP_PAE, RESTART); + sm->eapRestart = TRUE; +} + + +SM_STATE(SUPP_PAE, S_FORCE_AUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); + sm->suppPortStatus = Authorized; + sm->sPortMode = ForceAuthorized; +} + + +SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); + sm->suppPortStatus = Unauthorized; + sm->sPortMode = ForceUnauthorized; + eapol_sm_txLogoff(sm); +} + + +SM_STEP(SUPP_PAE) +{ + if ((sm->userLogoff && !sm->logoffSent) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); + else if (((sm->portControl == Auto) && + (sm->sPortMode != sm->portControl)) || + sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); + else if ((sm->portControl == ForceAuthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); + else if ((sm->portControl == ForceUnauthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); + else switch (sm->SUPP_PAE_state) { + case SUPP_PAE_UNKNOWN: + break; + case SUPP_PAE_LOGOFF: + if (!sm->userLogoff) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_DISCONNECTED: + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_CONNECTING: + if (sm->startWhen == 0 && sm->startCount < sm->maxStart) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapSuccess || sm->eapFail) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + !sm->portValid) + SM_ENTER(SUPP_PAE, HELD); + break; + case SUPP_PAE_AUTHENTICATING: + if (sm->eapSuccess && !sm->portValid && + sm->conf.accept_802_1x_keys && + sm->conf.required_keys == 0) { + wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " + "plaintext connection; no EAPOL-Key frames " + "required"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + if (sm->eapSuccess && sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapFail || (sm->keyDone && !sm->portValid)) + SM_ENTER(SUPP_PAE, HELD); + else if (sm->suppTimeout) + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_HELD: + if (sm->heldWhile == 0) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + break; + case SUPP_PAE_AUTHENTICATED: + if (sm->eapolEap && sm->portValid) + SM_ENTER(SUPP_PAE, RESTART); + else if (!sm->portValid) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_RESTART: + if (!sm->eapRestart) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + break; + case SUPP_PAE_S_FORCE_AUTH: + break; + case SUPP_PAE_S_FORCE_UNAUTH: + break; + } +} + + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, KEY_RECEIVE); + eapol_sm_processKey(sm); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + switch (sm->KEY_RX_state) { + case KEY_RX_UNKNOWN: + break; + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + +SM_STATE(SUPP_BE, REQUEST) +{ + SM_ENTRY(SUPP_BE, REQUEST); + sm->authWhile = 0; + sm->eapReq = TRUE; + eapol_sm_getSuppRsp(sm); +} + + +SM_STATE(SUPP_BE, RESPONSE) +{ + SM_ENTRY(SUPP_BE, RESPONSE); + eapol_sm_txSuppRsp(sm); + sm->eapResp = FALSE; +} + + +SM_STATE(SUPP_BE, SUCCESS) +{ + SM_ENTRY(SUPP_BE, SUCCESS); + sm->keyRun = TRUE; + sm->suppSuccess = TRUE; + + if (eap_key_available(sm->eap)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } +} + + +SM_STATE(SUPP_BE, FAIL) +{ + SM_ENTRY(SUPP_BE, FAIL); + sm->suppFail = TRUE; +} + + +SM_STATE(SUPP_BE, TIMEOUT) +{ + SM_ENTRY(SUPP_BE, TIMEOUT); + sm->suppTimeout = TRUE; +} + + +SM_STATE(SUPP_BE, IDLE) +{ + SM_ENTRY(SUPP_BE, IDLE); + sm->suppStart = FALSE; + sm->initial_req = TRUE; +} + + +SM_STATE(SUPP_BE, INITIALIZE) +{ + SM_ENTRY(SUPP_BE, INITIALIZE); + eapol_sm_abortSupp(sm); + sm->suppAbort = FALSE; +} + + +SM_STATE(SUPP_BE, RECEIVE) +{ + SM_ENTRY(SUPP_BE, RECEIVE); + sm->authWhile = sm->authPeriod; + eapol_enable_timer_tick(sm); + sm->eapolEap = FALSE; + sm->eapNoResp = FALSE; + sm->initial_req = FALSE; +} + + +SM_STEP(SUPP_BE) +{ + if (sm->initialize || sm->suppAbort) + SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); + else switch (sm->SUPP_BE_state) { + case SUPP_BE_UNKNOWN: + break; + case SUPP_BE_REQUEST: + /* + * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL + * and SUCCESS based on eapFail and eapSuccess, respectively. + * However, IEEE Std 802.1X-2004 is also specifying that + * eapNoResp should be set in conjuction with eapSuccess and + * eapFail which would mean that more than one of the + * transitions here would be activated at the same time. + * Skipping RESPONSE and/or RECEIVE states in these cases can + * cause problems and the direct transitions to do not seem + * correct. Because of this, the conditions for these + * transitions are verified only after eapNoResp. They are + * unlikely to be used since eapNoResp should always be set if + * either of eapSuccess or eapFail is set. + */ + if (sm->eapResp && sm->eapNoResp) { + wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " + "eapResp and eapNoResp set?!"); + } + if (sm->eapResp) + SM_ENTER(SUPP_BE, RESPONSE); + else if (sm->eapNoResp) + SM_ENTER(SUPP_BE, RECEIVE); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_RESPONSE: + SM_ENTER(SUPP_BE, RECEIVE); + break; + case SUPP_BE_SUCCESS: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_FAIL: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_TIMEOUT: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_IDLE: + if (sm->eapFail && sm->suppStart) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapolEap && sm->suppStart) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapSuccess && sm->suppStart) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_INITIALIZE: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_RECEIVE: + if (sm->eapolEap) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->authWhile == 0) + SM_ENTER(SUPP_BE, TIMEOUT); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + } +} + + +static void eapol_sm_txLogoff(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); + sm->dot1xSuppEapolLogoffFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_txStart(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txStart"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); + sm->dot1xSuppEapolStartFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +#define IEEE8021X_ENCR_KEY_LEN 32 +#define IEEE8021X_SIGN_KEY_LEN 32 + +struct eap_key_data { + u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; + u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; +}; + + +static void eapol_sm_processKey(struct eapol_sm *sm) +{ + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + struct eap_key_data keydata; + u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; + u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; + int key_len, res, sign_key_len, encr_key_len; + u16 rx_key_length; + + wpa_printf(MSG_DEBUG, "EAPOL: processKey"); + if (sm->last_rx_key == NULL) + return; + + if (!sm->conf.accept_802_1x_keys) { + wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" + " even though this was not accepted - " + "ignoring this packet"); + return; + } + + hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { + wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); + return; + } + rx_key_length = WPA_GET_BE16(key->key_length); + wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " + "EAPOL-Key: type=%d key_length=%d key_index=0x%x", + hdr->version, hdr->type, be_to_host16(hdr->length), + key->type, rx_key_length, key->key_index); + + eapol_sm_notify_lower_layer_success(sm, 1); + sign_key_len = IEEE8021X_SIGN_KEY_LEN; + encr_key_len = IEEE8021X_ENCR_KEY_LEN; + res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " + "decrypting EAPOL-Key keys"); + return; + } + if (res == 16) { + /* LEAP derives only 16 bytes of keying material. */ + res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); + if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " + "master key for decrypting EAPOL-Key keys"); + return; + } + sign_key_len = 16; + encr_key_len = 16; + os_memcpy(keydata.sign_key, keydata.encr_key, 16); + } else if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " + "data for decrypting EAPOL-Key keys (res=%d)", res); + return; + } + + /* The key replay_counter must increase when same master key */ + if (sm->replay_counter_valid && + os_memcmp(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { + wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " + "not increase - ignoring key"); + wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", + sm->last_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", + key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); + return; + } + + /* Verify key signature (HMAC-MD5) */ + os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); + os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); + hmac_md5(keydata.sign_key, sign_key_len, + sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), + key->key_signature); + if (os_memcmp(orig_key_sign, key->key_signature, + IEEE8021X_KEY_SIGN_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " + "EAPOL-Key packet"); + os_memcpy(key->key_signature, orig_key_sign, + IEEE8021X_KEY_SIGN_LEN); + return; + } + wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); + + key_len = be_to_host16(hdr->length) - sizeof(*key); + if (key_len > 32 || rx_key_length > 32) { + wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", + key_len ? key_len : rx_key_length); + return; + } + if (key_len == rx_key_length) { + os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); + os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, + encr_key_len); + os_memcpy(datakey, key + 1, key_len); + rc4(datakey, key_len, ekey, + IEEE8021X_KEY_IV_LEN + encr_key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", + datakey, key_len); + } else if (key_len == 0) { + /* + * IEEE 802.1X-2004 specifies that least significant Key Length + * octets from MS-MPPE-Send-Key are used as the key if the key + * data is not present. This seems to be meaning the beginning + * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in + * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. + * Anyway, taking the beginning of the keying material from EAP + * seems to interoperate with Authenticators. + */ + key_len = rx_key_length; + os_memcpy(datakey, keydata.encr_key, key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " + "material data encryption key", + datakey, key_len); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " + "(key_length=%d)", key_len, rx_key_length); + return; + } + + sm->replay_counter_valid = TRUE; + os_memcpy(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + + wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " + "len %d", + key->key_index & IEEE8021X_KEY_INDEX_FLAG ? + "unicast" : "broadcast", + key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); + + if (sm->ctx->set_wep_key && + sm->ctx->set_wep_key(sm->ctx->ctx, + key->key_index & IEEE8021X_KEY_INDEX_FLAG, + key->key_index & IEEE8021X_KEY_INDEX_MASK, + datakey, key_len) < 0) { + wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " + " driver."); + } else { + if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) + sm->unicast_key_received = TRUE; + else + sm->broadcast_key_received = TRUE; + + if ((sm->unicast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && + (sm->broadcast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) + { + wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " + "frames received"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + } +} + + +static void eapol_sm_getSuppRsp(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); + /* EAP layer processing; no special code is needed, since Supplicant + * Backend state machine is waiting for eapNoResp or eapResp to be set + * and these are only set in the EAP state machine when the processing + * has finished. */ +} + + +static void eapol_sm_txSuppRsp(struct eapol_sm *sm) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + resp = eap_get_eapRespData(sm->eap); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " + "not available"); + return; + } + + /* Send EAP-Packet from the EAP layer to the Authenticator */ + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), + wpabuf_len(resp)); + + /* eapRespData is not used anymore, so free it here */ + wpabuf_free(resp); + + if (sm->initial_req) + sm->dot1xSuppEapolReqIdFramesRx++; + else + sm->dot1xSuppEapolReqFramesRx++; + sm->dot1xSuppEapolRespFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_abortSupp(struct eapol_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + os_free(sm->last_rx_key); + sm->last_rx_key = NULL; + wpabuf_free(sm->eapReqData); + sm->eapReqData = NULL; + eap_sm_abort(sm->eap); +} + + +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) +{ + eapol_sm_step(timeout_ctx); +} + + +/** + * eapol_sm_step - EAPOL state machine step function + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function is called to notify the state machine about changed external + * variables. It will step through the EAPOL state machines in loop to process + * all triggered state changes. + */ +void eapol_sm_step(struct eapol_sm *sm) +{ + int i; + + /* In theory, it should be ok to run this in loop until !changed. + * However, it is better to use a limit on number of iterations to + * allow events (e.g., SIGTERM) to stop the program cleanly if the + * state machine were to generate a busy loop. */ + for (i = 0; i < 100; i++) { + sm->changed = FALSE; + SM_STEP_RUN(SUPP_PAE); + SM_STEP_RUN(KEY_RX); + SM_STEP_RUN(SUPP_BE); + if (eap_peer_sm_step(sm->eap)) + sm->changed = TRUE; + if (!sm->changed) + break; + } + + if (sm->changed) { + /* restart EAPOL state machine step from timeout call in order + * to allow other events to be processed. */ + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); + } + + if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { + int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + sm->cb_status = EAPOL_CB_IN_PROGRESS; + sm->ctx->cb(sm, success, sm->ctx->cb_ctx); + } +} + + +#ifdef CONFIG_CTRL_IFACE +static const char *eapol_supp_pae_state(int state) +{ + switch (state) { + case SUPP_PAE_LOGOFF: + return "LOGOFF"; + case SUPP_PAE_DISCONNECTED: + return "DISCONNECTED"; + case SUPP_PAE_CONNECTING: + return "CONNECTING"; + case SUPP_PAE_AUTHENTICATING: + return "AUTHENTICATING"; + case SUPP_PAE_HELD: + return "HELD"; + case SUPP_PAE_AUTHENTICATED: + return "AUTHENTICATED"; + case SUPP_PAE_RESTART: + return "RESTART"; + default: + return "UNKNOWN"; + } +} + + +static const char *eapol_supp_be_state(int state) +{ + switch (state) { + case SUPP_BE_REQUEST: + return "REQUEST"; + case SUPP_BE_RESPONSE: + return "RESPONSE"; + case SUPP_BE_SUCCESS: + return "SUCCESS"; + case SUPP_BE_FAIL: + return "FAIL"; + case SUPP_BE_TIMEOUT: + return "TIMEOUT"; + case SUPP_BE_IDLE: + return "IDLE"; + case SUPP_BE_INITIALIZE: + return "INITIALIZE"; + case SUPP_BE_RECEIVE: + return "RECEIVE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eapol_port_status(PortStatus status) +{ + if (status == Authorized) + return "Authorized"; + else + return "Unauthorized"; +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eapol_port_control(PortControl ctrl) +{ + switch (ctrl) { + case Auto: + return "Auto"; + case ForceUnauthorized: + return "ForceUnauthorized"; + case ForceAuthorized: + return "ForceAuthorized"; + default: + return "Unknown"; + } +} +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +/** + * eapol_sm_configure - Set EAPOL variables + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @heldPeriod: dot1xSuppHeldPeriod + * @authPeriod: dot1xSuppAuthPeriod + * @startPeriod: dot1xSuppStartPeriod + * @maxStart: dot1xSuppMaxStart + * + * Set configurable EAPOL state machine variables. Each variable can be set to + * the given value or ignored if set to -1 (to set only some of the variables). + */ +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart) +{ + if (sm == NULL) + return; + if (heldPeriod >= 0) + sm->heldPeriod = heldPeriod; + if (authPeriod >= 0) + sm->authPeriod = authPeriod; + if (startPeriod >= 0) + sm->startPeriod = startPeriod; + if (maxStart >= 0) + sm->maxStart = maxStart; +} + + +#ifdef CONFIG_CTRL_IFACE +/** + * eapol_sm_get_status - Get EAPOL state machine status + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose) +{ + int len, ret; + if (sm == NULL) + return 0; + + len = os_snprintf(buf, buflen, + "Supplicant PAE state=%s\n" + "suppPortStatus=%s\n", + eapol_supp_pae_state(sm->SUPP_PAE_state), + eapol_port_status(sm->suppPortStatus)); + if (len < 0 || (size_t) len >= buflen) + return 0; + + if (verbose) { + ret = os_snprintf(buf + len, buflen - len, + "heldPeriod=%u\n" + "authPeriod=%u\n" + "startPeriod=%u\n" + "maxStart=%u\n" + "portControl=%s\n" + "Supplicant Backend state=%s\n", + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + eapol_port_control(sm->portControl), + eapol_supp_be_state(sm->SUPP_BE_state)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); + + return len; +} + + +/** + * eapol_sm_get_mib - Get EAPOL state machine MIBs + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for MIB information + * @buflen: Maximum buffer length + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for MIB information. This function fills in a + * text area with current MIB information from the EAPOL state machine. If + * the buffer (buf) is not large enough, MIB information will be truncated to + * fit the buffer. + */ +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) +{ + size_t len; + int ret; + + if (sm == NULL) + return 0; + ret = os_snprintf(buf, buflen, + "dot1xSuppPaeState=%d\n" + "dot1xSuppHeldPeriod=%u\n" + "dot1xSuppAuthPeriod=%u\n" + "dot1xSuppStartPeriod=%u\n" + "dot1xSuppMaxStart=%u\n" + "dot1xSuppSuppControlledPortStatus=%s\n" + "dot1xSuppBackendPaeState=%d\n", + sm->SUPP_PAE_state, + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + sm->suppPortStatus == Authorized ? + "Authorized" : "Unauthorized", + sm->SUPP_BE_state); + + if (ret < 0 || (size_t) ret >= buflen) + return 0; + len = ret; + + ret = os_snprintf(buf + len, buflen - len, + "dot1xSuppEapolFramesRx=%u\n" + "dot1xSuppEapolFramesTx=%u\n" + "dot1xSuppEapolStartFramesTx=%u\n" + "dot1xSuppEapolLogoffFramesTx=%u\n" + "dot1xSuppEapolRespFramesTx=%u\n" + "dot1xSuppEapolReqIdFramesRx=%u\n" + "dot1xSuppEapolReqFramesRx=%u\n" + "dot1xSuppInvalidEapolFramesRx=%u\n" + "dot1xSuppEapLengthErrorFramesRx=%u\n" + "dot1xSuppLastEapolFrameVersion=%u\n" + "dot1xSuppLastEapolFrameSource=" MACSTR "\n", + sm->dot1xSuppEapolFramesRx, + sm->dot1xSuppEapolFramesTx, + sm->dot1xSuppEapolStartFramesTx, + sm->dot1xSuppEapolLogoffFramesTx, + sm->dot1xSuppEapolRespFramesTx, + sm->dot1xSuppEapolReqIdFramesRx, + sm->dot1xSuppEapolReqFramesRx, + sm->dot1xSuppInvalidEapolFramesRx, + sm->dot1xSuppEapLengthErrorFramesRx, + sm->dot1xSuppLastEapolFrameVersion, + MAC2STR(sm->dot1xSuppLastEapolFrameSource)); + + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +/** + * eapol_sm_rx_eapol - Process received EAPOL frames + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @src: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, + * -1 failure + */ +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len) +{ + const struct ieee802_1x_hdr *hdr; + const struct ieee802_1x_eapol_key *key; + int data_len; + int res = 1; + size_t plen; + + if (sm == NULL) + return 0; + sm->dot1xSuppEapolFramesRx++; + if (len < sizeof(*hdr)) { + sm->dot1xSuppInvalidEapolFramesRx++; + return 0; + } + hdr = (const struct ieee802_1x_hdr *) buf; + sm->dot1xSuppLastEapolFrameVersion = hdr->version; + os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + plen = be_to_host16(hdr->length); + if (plen > len - sizeof(*hdr)) { + sm->dot1xSuppEapLengthErrorFramesRx++; + return 0; + } +#ifdef CONFIG_WPS + if (sm->conf.workaround && + plen < len - sizeof(*hdr) && + hdr->type == IEEE802_1X_TYPE_EAP_PACKET && + len - sizeof(*hdr) > sizeof(struct eap_hdr)) { + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + u16 elen; + + elen = be_to_host16(ehdr->length); + if (elen > plen && elen <= len - sizeof(*hdr)) { + /* + * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS + * packets with too short EAPOL header length field + * (14 octets). This is fixed in firmware Ver.1.49. + * As a workaround, fix the EAPOL header based on the + * correct length in the EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " + "payload length based on EAP header: " + "%d -> %d", (int) plen, elen); + plen = elen; + } + } +#endif /* CONFIG_WPS */ + data_len = plen + sizeof(*hdr); + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->cached_pmk) { + /* Trying to use PMKSA caching, but Authenticator did + * not seem to have a matching entry. Need to restart + * EAPOL state machines. + */ + eapol_sm_abort_cached(sm); + } + wpabuf_free(sm->eapReqData); + sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); + if (sm->eapReqData) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " + "frame"); + sm->eapolEap = TRUE; + eapol_sm_step(sm); + } + break; + case IEEE802_1X_TYPE_EAPOL_KEY: + if (plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " + "frame received"); + break; + } + key = (const struct ieee802_1x_eapol_key *) (hdr + 1); + if (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN) { + /* WPA Supplicant takes care of this frame. */ + wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " + "frame in EAPOL state machines"); + res = 0; + break; + } + if (key->type != EAPOL_KEY_TYPE_RC4) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " + "EAPOL-Key type %d", key->type); + break; + } + os_free(sm->last_rx_key); + sm->last_rx_key = os_malloc(data_len); + if (sm->last_rx_key) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " + "frame"); + os_memcpy(sm->last_rx_key, buf, data_len); + sm->last_rx_key_len = data_len; + sm->rxKey = TRUE; + eapol_sm_step(sm); + } + break; + default: + wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", + hdr->type); + sm->dot1xSuppInvalidEapolFramesRx++; + break; + } + + return res; +} + + +/** + * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machine about transmitted EAPOL packet from an external + * component, e.g., WPA. This will update the statistics. + */ +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ + if (sm) + sm->dot1xSuppEapolFramesTx++; +} + + +/** + * eapol_sm_notify_portEnabled - Notification about portEnabled change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @enabled: New portEnabled value + * + * Notify EAPOL state machine about new portEnabled value. + */ +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portEnabled=%d", enabled); + sm->portEnabled = enabled; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_portValid - Notification about portValid change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @valid: New portValid value + * + * Notify EAPOL state machine about new portValid value. + */ +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portValid=%d", valid); + sm->portValid = valid; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_success - Notification of external EAP success trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @success: %TRUE = set success, %FALSE = clear success + * + * Notify the EAPOL state machine that external event has forced EAP state to + * success (success = %TRUE). This can be cleared by setting success = %FALSE. + * + * This function is called to update EAP state when WPA-PSK key handshake has + * been completed successfully since WPA-PSK does not use EAP state machine. + */ +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP success=%d", success); + sm->eapSuccess = success; + sm->altAccept = success; + if (success) + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @fail: %TRUE = set failure, %FALSE = clear failure + * + * Notify EAPOL state machine that external event has forced EAP state to + * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. + */ +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP fail=%d", fail); + sm->eapFail = fail; + sm->altReject = fail; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_config - Notification of EAPOL configuration change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @config: Pointer to current network EAP configuration + * @conf: Pointer to EAPOL configuration data + * + * Notify EAPOL state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. conf will be copied to local EAPOL/EAP configuration + * data. If conf is %NULL, this part of the configuration change will be + * skipped. + */ +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf) +{ + if (sm == NULL) + return; + + sm->config = config; + + if (conf == NULL) + return; + + sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; + sm->conf.required_keys = conf->required_keys; + sm->conf.fast_reauth = conf->fast_reauth; + sm->conf.workaround = conf->workaround; + 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) +{ + const u8 *eap_key; + size_t eap_len; + + if (sm == NULL || !eap_key_available(sm->eap)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); + return -1; + } + eap_key = eap_get_eapKeyData(sm->eap, &eap_len); + if (eap_key == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); + return -1; + } + if (len > eap_len) { + wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " + "available (len=%lu)", + (unsigned long) len, (unsigned long) eap_len); + return eap_len; + } + os_memcpy(key, eap_key, len); + wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", + (unsigned long) len); + return 0; +} + + +/** + * eapol_sm_notify_logoff - Notification of logon/logoff commands + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @logoff: Whether command was logoff + * + * Notify EAPOL state machines that user requested logon/logoff. + */ +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ + if (sm) { + sm->userLogoff = logoff; + eapol_sm_step(sm); + } +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that PMKSA caching was successful. This is used + * to move EAPOL and EAP state machines into authenticated/successful state. + */ +void eapol_sm_notify_cached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); + sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; + sm->suppPortStatus = Authorized; + sm->portValid = TRUE; + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @attempt: Whether PMKSA caching is tried + * + * Notify EAPOL state machines whether PMKSA caching is used. + */ +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) +{ + if (sm == NULL) + return; + if (attempt) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; + } else { + wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); + sm->cached_pmk = FALSE; + } +} + + +static void eapol_sm_abort_cached(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " + "doing full EAP authentication"); + if (sm == NULL) + return; + sm->cached_pmk = FALSE; + sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; + sm->suppPortStatus = Unauthorized; + + /* Make sure we do not start sending EAPOL-Start frames first, but + * instead move to RESTART state to start EAPOL authentication. */ + sm->startWhen = 3; + eapol_enable_timer_tick(sm); + + if (sm->ctx->aborted_cached) + sm->ctx->aborted_cached(sm->ctx->ctx); +} + + +/** + * eapol_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAPOL state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) +{ + if (sm) { + sm->ctx->scard_ctx = ctx; + eap_register_scard_ctx(sm->eap, ctx); + } +} + + +/** + * eapol_sm_notify_portControl - Notification of portControl changes + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @portControl: New value for portControl variable + * + * Notify EAPOL state machines that portControl variable has changed. + */ +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portControl=%s", eapol_port_control(portControl)); + sm->portControl = portControl; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eap_sm_notify_ctrl_attached(sm->eap); +} + + +/** + * eapol_sm_notify_ctrl_response - Notification of received user input + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a control response, i.e., user + * input, was received in order to trigger retrying of a pending EAP request. + */ +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " + "input) notification - retrying pending EAP " + "Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +/** + * eapol_sm_request_reauth - Request reauthentication + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function can be used to request EAPOL reauthentication, e.g., when the + * current PMKSA entry is nearing expiration. + */ +void eapol_sm_request_reauth(struct eapol_sm *sm) +{ + if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) + return; + eapol_sm_txStart(sm); +} + + +/** + * eapol_sm_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @in_eapol_sm: Whether the caller is already running inside EAPOL state + * machine loop (eapol_sm_step()) + * + * Notify EAPOL (and EAP) state machines that a lower layer has detected a + * successful authentication. This is used to recover from dropped EAP-Success + * messages. + */ +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) +{ + if (sm == NULL) + return; + eap_notify_lower_layer_success(sm->eap); + if (!in_eapol_sm) + eapol_sm_step(sm); +} + + +/** + * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) +{ + if (sm) + eap_invalidate_cached_session(sm->eap); +} + + +static struct eap_peer_config * eapol_sm_get_config(void *ctx) +{ + struct eapol_sm *sm = ctx; + return sm ? sm->config : NULL; +} + + +static struct wpabuf * eapol_sm_get_eapReqData(void *ctx) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL || sm->eapReqData == NULL) + return NULL; + + return sm->eapReqData; +} + + +static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return FALSE; + switch (variable) { + case EAPOL_eapSuccess: + return sm->eapSuccess; + case EAPOL_eapRestart: + return sm->eapRestart; + case EAPOL_eapFail: + return sm->eapFail; + case EAPOL_eapResp: + return sm->eapResp; + case EAPOL_eapNoResp: + return sm->eapNoResp; + case EAPOL_eapReq: + return sm->eapReq; + case EAPOL_portEnabled: + return sm->portEnabled; + case EAPOL_altAccept: + return sm->altAccept; + case EAPOL_altReject: + return sm->altReject; + } + return FALSE; +} + + +static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, + Boolean value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_eapSuccess: + sm->eapSuccess = value; + break; + case EAPOL_eapRestart: + sm->eapRestart = value; + break; + case EAPOL_eapFail: + sm->eapFail = value; + break; + case EAPOL_eapResp: + sm->eapResp = value; + break; + case EAPOL_eapNoResp: + sm->eapNoResp = value; + break; + case EAPOL_eapReq: + sm->eapReq = value; + break; + case EAPOL_portEnabled: + sm->portEnabled = value; + break; + case EAPOL_altAccept: + sm->altAccept = value; + break; + case EAPOL_altReject: + sm->altReject = value; + break; + } +} + + +static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return 0; + switch (variable) { + case EAPOL_idleWhile: + return sm->idleWhile; + } + return 0; +} + + +static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, + unsigned int value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_idleWhile: + sm->idleWhile = value; + eapol_enable_timer_tick(sm); + break; + } +} + + +static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->set_config_blob) + sm->ctx->set_config_blob(sm->ctx->ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static const struct wpa_config_blob * +eapol_sm_get_config_blob(void *ctx, const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->get_config_blob) + return sm->ctx->get_config_blob(sm->ctx->ctx, name); + else + return NULL; +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static void eapol_sm_notify_pending(void *ctx) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " + "state machine - retrying pending EAP Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_sm_eap_param_needed(void *ctx, const char *field, + const char *txt) +{ + struct eapol_sm *sm = ctx; + wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); + if (sm->ctx->eap_param_needed) + sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_sm_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +static struct eapol_callbacks eapol_cb = +{ + eapol_sm_get_config, + eapol_sm_get_bool, + eapol_sm_set_bool, + eapol_sm_get_int, + eapol_sm_set_int, + eapol_sm_get_eapReqData, + eapol_sm_set_config_blob, + eapol_sm_get_config_blob, + eapol_sm_notify_pending, + eapol_sm_eap_param_needed +}; + + +/** + * eapol_sm_init - Initialize EAPOL state machine + * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer + * and EAPOL state machine will free it in eapol_sm_deinit() + * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure + * + * Allocate and initialize an EAPOL state machine. + */ +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + struct eapol_sm *sm; + struct eap_config conf; + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->ctx = ctx; + + sm->portControl = Auto; + + /* Supplicant PAE state machine */ + sm->heldPeriod = 60; + sm->startPeriod = 30; + sm->maxStart = 3; + + /* Supplicant Backend state machine */ + sm->authPeriod = 30; + + os_memset(&conf, 0, sizeof(conf)); +#ifdef EAP_TLS_OPENSSL + conf.opensc_engine_path = ctx->opensc_engine_path; + conf.pkcs11_engine_path = ctx->pkcs11_engine_path; + conf.pkcs11_module_path = ctx->pkcs11_module_path; +#endif /* EAP_TLS_OPENSSL */ + conf.wps = ctx->wps; + + sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); + if (sm->eap == NULL) { + os_free(sm); + return NULL; + } + + /* Initialize EAPOL state machines */ + sm->initialize = TRUE; + eapol_sm_step(sm); + sm->initialize = FALSE; + eapol_sm_step(sm); + + sm->timer_tick_enabled = 1; + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + + return sm; +} + + +/** + * eapol_sm_deinit - Deinitialize EAPOL state machine + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Deinitialize and free EAPOL state machine. + */ +void eapol_sm_deinit(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eap_peer_sm_deinit(sm->eap); + os_free(sm->last_rx_key); + wpabuf_free(sm->eapReqData); + os_free(sm->ctx); + os_free(sm); +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h new file mode 100644 index 0000000000000..57d7bc1ab12e1 --- /dev/null +++ b/src/eapol_supp/eapol_supp_sm.h @@ -0,0 +1,342 @@ +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAPOL_SUPP_SM_H +#define EAPOL_SUPP_SM_H + +#include "defs.h" + +typedef enum { Unauthorized, Authorized } PortStatus; +typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; + +/** + * struct eapol_config - Per network configuration for EAPOL state machines + */ +struct eapol_config { + /** + * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames + * + * This variable should be set to 1 when using EAPOL state machines + * with non-WPA security policy to generate dynamic WEP keys. When + * using WPA, this should be set to 0 so that WPA state machine can + * process the EAPOL-Key frames. + */ + int accept_802_1x_keys; + +#define EAPOL_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) + /** + * required_keys - Which EAPOL-Key packets are required + * + * This variable determines which EAPOL-Key packets are required before + * marking connection authenticated. This is a bit field of + * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags. + */ + int required_keys; + + /** + * fast_reauth - Whether fast EAP reauthentication is enabled + */ + int fast_reauth; + + /** + * workaround - Whether EAP workarounds are enabled + */ + unsigned int workaround; + + /** + * eap_disabled - Whether EAP is disabled + */ + int eap_disabled; +}; + +struct eapol_sm; +struct wpa_config_blob; + +/** + * struct eapol_ctx - Global (for all networks) EAPOL state machine context + */ +struct eapol_ctx { + /** + * ctx - Pointer to arbitrary upper level context + */ + void *ctx; + + /** + * preauth - IEEE 802.11i/RSN pre-authentication + * + * This EAPOL state machine is used for IEEE 802.11i/RSN + * pre-authentication + */ + int preauth; + + /** + * cb - Function to be called when EAPOL negotiation has been completed + * @eapol: Pointer to EAPOL state machine data + * @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); + + /** + * cb_ctx - Callback context for cb() + */ + void *cb_ctx; + + /** + * msg_ctx - Callback context for wpa_msg() calls + */ + void *msg_ctx; + + /** + * scard_ctx - Callback context for PC/SC scard_*() function calls + * + * This context can be updated with eapol_sm_register_scard_ctx(). + */ + void *scard_ctx; + + /** + * eapol_send_ctx - Callback context for eapol_send() calls + */ + void *eapol_send_ctx; + + /** + * eapol_done_cb - Function to be called at successful completion + * @ctx: Callback context (ctx) + * + * This function is called at the successful completion of EAPOL + * authentication. If dynamic WEP keys are used, this is called only + * after all the expected keys have been received. + */ + void (*eapol_done_cb)(void *ctx); + + /** + * eapol_send - Send EAPOL packets + * @ctx: Callback context (eapol_send_ctx) + * @type: EAPOL type (IEEE802_1X_TYPE_*) + * @buf: Pointer to EAPOL payload + * @len: Length of the EAPOL payload + * Returns: 0 on success, -1 on failure + */ + int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len); + + /** + * set_wep_key - Configure WEP keys + * @ctx: Callback context (ctx) + * @unicast: Non-zero = unicast, 0 = multicast/broadcast key + * @keyidx: Key index (0..3) + * @key: WEP key + * @keylen: Length of the WEP key + * Returns: 0 on success, -1 on failure + */ + int (*set_wep_key)(void *ctx, int unicast, int keyidx, + const u8 *key, size_t keylen); + + /** + * set_config_blob - Set or add a named configuration blob + * @ctx: Callback context (ctx) + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: Callback context (ctx) + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * aborted_cached - Notify that cached PMK attempt was aborted + * @ctx: Callback context (ctx) + */ + void (*aborted_cached)(void *ctx); + +#ifdef EAP_TLS_OPENSSL + /** + * 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; +#endif /* EAP_TLS_OPENSSL */ + + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: Callback context (ctx) + * @field: Field name (e.g., "IDENTITY") + * @txt: User readable text describing the required parameter + */ + void (*eap_param_needed)(void *ctx, const char *field, + const char *txt); +}; + + +struct eap_peer_config; + +#ifdef IEEE8021X_EAPOL +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); +void eapol_sm_deinit(struct eapol_sm *sm); +void eapol_sm_step(struct eapol_sm *sm); +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose); +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart); +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len); +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf); +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); +void eapol_sm_notify_cached(struct eapol_sm *sm); +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt); +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); +void eapol_sm_request_reauth(struct eapol_sm *sm); +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); +#else /* IEEE8021X_EAPOL */ +static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + free(ctx); + return (struct eapol_sm *) 1; +} +static inline void eapol_sm_deinit(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_step(struct eapol_sm *sm) +{ +} +static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} +static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, + size_t buflen) +{ + return 0; +} +static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, + int authPeriod, int startPeriod, + int maxStart) +{ +} +static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, + const u8 *buf, size_t len) +{ + return 0; +} +static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm, + Boolean enabled) +{ +} +static inline void eapol_sm_notify_portValid(struct eapol_sm *sm, + Boolean valid) +{ +} +static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm, + Boolean success) +{ +} +static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ +} +static inline void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + struct eapol_config *conf) +{ +} +static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + return -1; +} +static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ +} +static inline void eapol_sm_notify_cached(struct eapol_sm *sm) +{ +} +#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) +#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) +static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, + PortControl portControl) +{ +} +static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_request_reauth(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, + int in_eapol_sm) +{ +} +static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) +{ +} +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAPOL_SUPP_SM_H */ diff --git a/src/hlr_auc_gw/.gitignore b/src/hlr_auc_gw/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/hlr_auc_gw/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/hlr_auc_gw/Makefile b/src/hlr_auc_gw/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/hlr_auc_gw/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/hlr_auc_gw.c b/src/hlr_auc_gw/hlr_auc_gw.c index a587702c5029d..3d914e60cb8c7 100644 --- a/contrib/hostapd/hlr_auc_gw.c +++ b/src/hlr_auc_gw/hlr_auc_gw.c @@ -1,6 +1,6 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -43,13 +43,7 @@ * implementation and for EAP-SIM testing. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <signal.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> +#include "includes.h" #include <sys/un.h> #include "common.h" @@ -57,10 +51,19 @@ static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; static const char *socket_path; -static const char *default_gsm_triplet_file = "hostapd.sim_db"; -static const char *gsm_triplet_file; static int serv_sock = -1; +/* GSM triplets */ +struct gsm_triplet { + struct gsm_triplet *next; + char imsi[20]; + u8 kc[8]; + u8 sres[4]; + u8 _rand[16]; +}; + +static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; + /* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ struct milenage_parameters { struct milenage_parameters *next; @@ -96,7 +99,7 @@ static int open_socket(const char *path) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(PF_UNIX)"); close(s); @@ -107,6 +110,144 @@ static int open_socket(const char *path) } +static int read_gsm_triplets(const char *fname) +{ + FILE *f; + char buf[200], *pos, *pos2; + struct gsm_triplet *g = NULL; + int line, ret = 0; + + if (fname == NULL) + return -1; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open GSM tripler data file '%s'\n", fname); + return -1; + } + + line = 0; + while (fgets(buf, sizeof(buf), f)) { + line++; + + /* Parse IMSI:Kc:SRES:RAND */ + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + pos = buf; + if (*pos == '\0') + continue; + + g = os_zalloc(sizeof(*g)); + if (g == NULL) { + ret = -1; + break; + } + + /* IMSI */ + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + printf("%s:%d - Invalid IMSI (%s)\n", + fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) >= sizeof(g->imsi)) { + printf("%s:%d - Too long IMSI (%s)\n", + fname, line, pos); + ret = -1; + break; + } + os_strlcpy(g->imsi, pos, sizeof(g->imsi)); + pos = pos2 + 1; + + /* Kc */ + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { + printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); + ret = -1; + break; + } + pos = pos2 + 1; + + /* SRES */ + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + printf("%s:%d - Invalid SRES (%s)\n", fname, line, + pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { + printf("%s:%d - Invalid SRES (%s)\n", fname, line, + pos); + ret = -1; + break; + } + pos = pos2 + 1; + + /* RAND */ + pos2 = strchr(pos, ':'); + if (pos2) + *pos2 = '\0'; + if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { + printf("%s:%d - Invalid RAND (%s)\n", fname, line, + pos); + ret = -1; + break; + } + pos = pos2 + 1; + + g->next = gsm_db; + gsm_db = g; + g = NULL; + } + free(g); + + fclose(f); + + return ret; +} + + +static struct gsm_triplet * get_gsm_triplet(const char *imsi) +{ + struct gsm_triplet *g = gsm_db_pos; + + while (g) { + if (strcmp(g->imsi, imsi) == 0) { + gsm_db_pos = g->next; + return g; + } + g = g->next; + } + + g = gsm_db; + while (g && g != gsm_db_pos) { + if (strcmp(g->imsi, imsi) == 0) { + gsm_db_pos = g->next; + return g; + } + g = g->next; + } + + return NULL; +} + + static int read_milenage(const char *fname) { FILE *f; @@ -161,7 +302,7 @@ static int read_milenage(const char *fname) ret = -1; break; } - strncpy(m->imsi, pos, sizeof(m->imsi)); + os_strlcpy(m->imsi, pos, sizeof(m->imsi)); pos = pos2 + 1; /* Ki */ @@ -249,11 +390,11 @@ static struct milenage_parameters * get_milenage(const char *imsi) static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, char *imsi) { - FILE *f; int count, max_chal, ret; - char buf[80], *pos; + char *pos; char reply[1000], *rpos, *rend; struct milenage_parameters *m; + struct gsm_triplet *g; reply[0] = '\0'; @@ -277,7 +418,8 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, if (m) { u8 _rand[16], sres[4], kc[8]; for (count = 0; count < max_chal; count++) { - os_get_random(_rand, 16); + if (os_get_random(_rand, 16) < 0) + return; gsm_milenage(m->opc, m->ki, _rand, sres, kc); *rpos++ = ' '; rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); @@ -290,50 +432,23 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, goto send; } - /* TODO: could read triplet file into memory during startup and then - * have pointer for IMSI to allow more than three first entries to be - * used. */ - f = fopen(gsm_triplet_file, "r"); - if (f == NULL) { - printf("Could not open GSM triplet file '%s'\n", - gsm_triplet_file); - ret = snprintf(rpos, rend - rpos, " FAILURE"); - if (ret < 0 || ret >= rend - rpos) - return; - rpos += ret; - goto send; - } - count = 0; - while (count < max_chal && fgets(buf, sizeof(buf), f)) { - /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */ - buf[sizeof(buf) - 1] = '\0'; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - if (pos - buf < 60 || pos[0] == '#') - continue; - - pos = strchr(buf, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - if (strcmp(buf, imsi) != 0) + while (count < max_chal && (g = get_gsm_triplet(imsi))) { + if (strcmp(g->imsi, imsi) != 0) continue; - ret = snprintf(rpos, rend - rpos, " %s", pos); - if (ret < 0 || ret >= rend - rpos) { - fclose(f); - return; - } - rpos += ret; + if (rpos < rend) + *rpos++ = ' '; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); + if (rpos < rend) + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); + if (rpos < rend) + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); count++; } - fclose(f); - if (count == 0) { printf("No GSM triplets found for %s\n", imsi); ret = snprintf(rpos, rend - rpos, " FAILURE"); @@ -366,7 +481,8 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, m = get_milenage(imsi); if (m) { - os_get_random(_rand, EAP_AKA_RAND_LEN); + if (os_get_random(_rand, EAP_AKA_RAND_LEN) < 0) + return; res_len = EAP_AKA_RES_MAX_LEN; inc_byte_array(m->sqn, 6); printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", @@ -495,8 +611,16 @@ static int process(int s) static void cleanup(void) { + struct gsm_triplet *g, *gprev; struct milenage_parameters *m, *prev; + g = gsm_db; + while (g) { + gprev = g; + g = g->next; + free(gprev); + } + m = milenage_db; while (m) { prev = m; @@ -520,7 +644,7 @@ static void usage(void) { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>\n" + "Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>\n" "\n" "usage:\n" "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] " @@ -531,9 +655,8 @@ static void usage(void) " -s<socket path> = path for UNIX domain socket\n" " (default: %s)\n" " -g<triplet file> = path for GSM authentication triplets\n" - " (default: %s)\n" " -m<milenage file> = path for Milenage keys\n", - default_socket_path, default_gsm_triplet_file); + default_socket_path); } @@ -541,9 +664,9 @@ int main(int argc, char *argv[]) { int c; char *milenage_file = NULL; + char *gsm_triplet_file = NULL; socket_path = default_socket_path; - gsm_triplet_file = default_gsm_triplet_file; for (;;) { c = getopt(argc, argv, "g:hm:s:"); @@ -568,6 +691,9 @@ int main(int argc, char *argv[]) } } + if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) + return -1; + if (milenage_file && read_milenage(milenage_file) < 0) return -1; diff --git a/contrib/hostapd/hlr_auc_gw.milenage_db b/src/hlr_auc_gw/hlr_auc_gw.milenage_db index fa15d53853b69..ecd06d72096d1 100644 --- a/contrib/hostapd/hlr_auc_gw.milenage_db +++ b/src/hlr_auc_gw/hlr_auc_gw.milenage_db @@ -7,3 +7,7 @@ # IMSI Ki OPc AMF SQN 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 + +# These values are from Test Set 19 which has the AMF separation bit set to 1 +# and as such, is suitable for EAP-AKA' test. +555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1 diff --git a/contrib/hostapd/milenage.c b/src/hlr_auc_gw/milenage.c index ab8bbd5e4f918..0ce5ef3ff671e 100644 --- a/contrib/hostapd/milenage.c +++ b/src/hlr_auc_gw/milenage.c @@ -1,6 +1,6 @@ /* * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) - * Copyright (c) 2006 <j@w1.fi> + * Copyright (c) 2006-2007 <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -37,9 +37,10 @@ * @amf: AMF = 16-bit authentication management field * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL + * Returns: 0 on success, -1 on failure */ -static void milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, - const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) +static int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; @@ -47,12 +48,13 @@ static void milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; - aes_128_encrypt_block(k, tmp1, tmp1); + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ - memcpy(tmp2, sqn, 6); - memcpy(tmp2 + 6, amf, 2); - memcpy(tmp2 + 8, tmp2, 8); + os_memcpy(tmp2, sqn, 6); + os_memcpy(tmp2 + 6, amf, 2); + os_memcpy(tmp2 + 8, tmp2, 8); /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ @@ -65,13 +67,15 @@ static void milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, /* XOR with c1 (= ..00, i.e., NOP) */ /* f1 || f1* = E_K(tmp3) XOR OP_c */ - aes_128_encrypt_block(k, tmp3, tmp1); + if (aes_128_encrypt_block(k, tmp3, tmp1)) + return -1; for (i = 0; i < 16; i++) tmp1[i] ^= opc[i]; if (mac_a) - memcpy(mac_a, tmp1, 8); /* f1 */ + os_memcpy(mac_a, tmp1, 8); /* f1 */ if (mac_s) - memcpy(mac_s, tmp1 + 8, 8); /* f1* */ + os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ + return 0; } @@ -85,9 +89,10 @@ static void milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL + * Returns: 0 on success, -1 on failure */ -static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, - u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) +static int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; @@ -95,7 +100,8 @@ static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; - aes_128_encrypt_block(k, tmp1, tmp2); + if (aes_128_encrypt_block(k, tmp1, tmp2)) + return -1; /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ @@ -108,13 +114,14 @@ static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, tmp1[i] = tmp2[i] ^ opc[i]; tmp1[15] ^= 1; /* XOR c2 (= ..01) */ /* f5 || f2 = E_K(tmp1) XOR OP_c */ - aes_128_encrypt_block(k, tmp1, tmp3); + if (aes_128_encrypt_block(k, tmp1, tmp3)) + return -1; for (i = 0; i < 16; i++) tmp3[i] ^= opc[i]; if (res) - memcpy(res, tmp3 + 8, 8); /* f2 */ + os_memcpy(res, tmp3 + 8, 8); /* f2 */ if (ak) - memcpy(ak, tmp3, 6); /* f5 */ + os_memcpy(ak, tmp3, 6); /* f5 */ /* f3 */ if (ck) { @@ -122,7 +129,8 @@ static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, for (i = 0; i < 16; i++) tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 2; /* XOR c3 (= ..02) */ - aes_128_encrypt_block(k, tmp1, ck); + if (aes_128_encrypt_block(k, tmp1, ck)) + return -1; for (i = 0; i < 16; i++) ck[i] ^= opc[i]; } @@ -133,7 +141,8 @@ static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, for (i = 0; i < 16; i++) tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 4; /* XOR c4 (= ..04) */ - aes_128_encrypt_block(k, tmp1, ik); + if (aes_128_encrypt_block(k, tmp1, ik)) + return -1; for (i = 0; i < 16; i++) ik[i] ^= opc[i]; } @@ -144,10 +153,13 @@ static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, for (i = 0; i < 16; i++) tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 8; /* XOR c5 (= ..08) */ - aes_128_encrypt_block(k, tmp1, tmp1); + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; for (i = 0; i < 6; i++) akstar[i] = tmp1[i] ^ opc[i]; } + + return 0; } @@ -169,21 +181,24 @@ void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, u8 *ck, u8 *res, size_t *res_len) { int i; - u8 mac_a[16], ak[6]; + u8 mac_a[8], ak[6]; if (*res_len < 8) { *res_len = 0; return; } + if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || + milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { + *res_len = 0; + return; + } *res_len = 8; - milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL); - milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL); /* AUTN = (SQN ^ AK) || AMF || MAC */ for (i = 0; i < 6; i++) autn[i] = sqn[i] ^ ak[i]; - memcpy(autn + 6, amf, 2); - memcpy(autn + 8, mac_a, 8); + os_memcpy(autn + 6, amf, 2); + os_memcpy(autn + 8, mac_a, 8); } @@ -203,11 +218,12 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 ak[6], mac_s[8]; int i; - milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak); + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; for (i = 0; i < 6; i++) sqn[i] = auts[i] ^ ak[i]; - milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s); - if (memcmp(mac_s, auts + 6, 8) != 0) + if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || + memcmp(mac_s, auts + 6, 8) != 0) return -1; return 0; } @@ -220,24 +236,96 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, * @_rand: RAND = 128-bit random challenge * @sres: Buffer for SRES = 32-bit SRES * @kc: Buffer for Kc = 64-bit Kc + * Returns: 0 on success, -1 on failure */ -void gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, - u8 *kc) +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) { u8 res[8], ck[16], ik[16]; int i; - milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL); + if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) + return -1; for (i = 0; i < 8; i++) kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; #ifdef GSM_MILENAGE_ALT_SRES - memcpy(sres, res, 4); + os_memcpy(sres, res, 4); #else /* GSM_MILENAGE_ALT_SRES */ for (i = 0; i < 4; i++) sres[i] = res[i] ^ res[i + 4]; #endif /* GSM_MILENAGE_ALT_SRES */ + return 0; +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Variable that will be set to RES length + * @auts: 112-bit buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 on synchronization failure + */ +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts) +{ + int i; + u8 mac_a[8], ak[6], rx_sqn[6]; + const u8 *amf; + + wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); + wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); + + if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) + return -1; + + *res_len = 8; + wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); + wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); + wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); + wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + rx_sqn[i] = autn[i] ^ ak[i]; + wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); + + if (os_memcmp(rx_sqn, sqn, 6) <= 0) { + u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); + for (i = 0; i < 6; i++) + auts[i] = sqn[i] ^ ak[i]; + if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) + return -1; + wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); + return -2; + } + + amf = autn + 6; + wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); + if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) + return -1; + + wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); + + if (os_memcmp(mac_a, autn + 8, 8) != 0) { + wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); + wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", + autn + 8, 8); + return -1; + } + + return 0; } @@ -953,8 +1041,8 @@ int main(int argc, char *argv[]) ret++; } - milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2); - if (memcmp(buf, t->f1, 8) != 0) { + if (milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2) + || memcmp(buf, t->f1, 8) != 0) { printf("- milenage_f1 failed\n"); ret++; } @@ -963,9 +1051,9 @@ int main(int argc, char *argv[]) ret++; } - milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4, - buf5); - if (memcmp(buf, t->f2, 8) != 0) { + if (milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4, + buf5) || + memcmp(buf, t->f2, 8) != 0) { printf("- milenage_f2 failed\n"); ret++; } @@ -988,17 +1076,18 @@ int main(int argc, char *argv[]) } printf("milenage_auts test:\n"); - memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6); - memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8); + os_memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6); + os_memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8); res = milenage_auts(t->opc, t->k, t->rand, auts, buf); printf("AUTS for test set %d: %d / SQN=%02x%02x%02x%02x%02x%02x\n", i, res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); if (res) ret++; - memset(_rand, 0xaa, sizeof(_rand)); - memcpy(auts, - "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67", 14); + os_memset(_rand, 0xaa, sizeof(_rand)); + os_memcpy(auts, + "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67", + 14); res = milenage_auts(t->opc, t->k, _rand, auts, buf); printf("AUTS from a test USIM: %d / SQN=%02x%02x%02x%02x%02x%02x\n", res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); @@ -1006,9 +1095,9 @@ int main(int argc, char *argv[]) ret++; printf("milenage_generate test:\n"); - memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6); - memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84" - "\x4f\xe6\x2f", 16); + os_memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6); + os_memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84" + "\x4f\xe6\x2f", 16); res_len = 8; milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3, buf4, &res_len); diff --git a/contrib/hostapd/milenage.h b/src/hlr_auc_gw/milenage.h index cc184f0f025b0..b35603ca8698e 100644 --- a/contrib/hostapd/milenage.h +++ b/src/hlr_auc_gw/milenage.h @@ -1,6 +1,6 @@ /* * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) - * Copyright (c) 2006 <j@w1.fi> + * Copyright (c) 2006-2007 <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,7 +20,10 @@ void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, u8 *ck, u8 *res, size_t *res_len); int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 *sqn); -void gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, - u8 *kc); +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, + u8 *kc); +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts); #endif /* MILENAGE_H */ diff --git a/src/l2_packet/.gitignore b/src/l2_packet/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/l2_packet/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/l2_packet/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/l2_packet.h b/src/l2_packet/l2_packet.h index 540f0a116b568..c7b50141e9c37 100644 --- a/contrib/hostapd/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -21,17 +21,6 @@ #ifndef L2_PACKET_H #define L2_PACKET_H -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" - -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - -#ifndef ETH_P_RSN_PREAUTH -#define ETH_P_RSN_PREAUTH 0x88c7 -#endif - /** * struct l2_packet_data - Internal l2_packet data structure * @@ -49,7 +38,7 @@ struct l2_packet_data; struct l2_ethhdr { u8 h_dest[ETH_ALEN]; u8 h_source[ETH_ALEN]; - u16 h_proto; + be16 h_proto; } STRUCT_PACKED; #ifdef _MSC_VER diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c new file mode 100644 index 0000000000000..d1034aa762714 --- /dev/null +++ b/src/l2_packet/l2_packet_freebsd.c @@ -0,0 +1,285 @@ +/* + * WPA Supplicant - Layer2 packet handling with FreeBSD + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005, Sam Leffler <sam@errno.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifdef __APPLE__ +#include <net/bpf.h> +#endif /* __APPLE__ */ +#include <pcap.h> + +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (!l2->l2_hdr) { + int ret; + struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len); + if (eth == NULL) + return -1; + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth)); + os_free(eth); + return ret; + } else + return pcap_inject(l2->pcap, buf, len); +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } + + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); + + return 0; +} + + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + u_char *p, *buf; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (eth_get(l2->ifname, l2->own_addr) < 0) { + fprintf(stderr, "Failed to get link-level address for " + "interface '%s'.\n", l2->ifname); + os_free(l2); + return NULL; + } + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 != NULL) { + if (l2->pcap) { + eloop_unregister_read_sock( + pcap_get_selectable_fd(l2->pcap)); + pcap_close(l2->pcap); + } + os_free(l2); + } +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c new file mode 100644 index 0000000000000..9def7ffc46894 --- /dev/null +++ b/src/l2_packet/l2_packet_linux.c @@ -0,0 +1,199 @@ +/* + * WPA Supplicant - Layer2 packet handling with Linux packet sockets + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <netpacket/packet.h> +#include <net/if.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + int fd; /* packet socket for EAPOL frames */ + char ifname[IFNAMSIZ + 1]; + int ifindex; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + if (l2 == NULL) + return -1; + if (l2->l2_hdr) { + ret = send(l2->fd, buf, len, 0); + if (ret < 0) + perror("l2_packet_send - send"); + } else { + struct sockaddr_ll ll; + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = l2->ifindex; + ll.sll_protocol = htons(proto); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); + ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, + sizeof(ll)); + if (ret < 0) + perror("l2_packet_send - sendto"); + } + return ret; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_ll ll; + socklen_t fromlen; + + os_memset(&ll, 0, sizeof(ll)); + fromlen = sizeof(ll); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, + &fromlen); + if (res < 0) { + perror("l2_packet_receive - recvfrom"); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + struct ifreq ifr; + struct sockaddr_ll ll; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(protocol)); + if (l2->fd < 0) { + perror("socket(PF_PACKET)"); + os_free(l2); + return NULL; + } + os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); + if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { + perror("ioctl[SIOCGIFINDEX]"); + close(l2->fd); + os_free(l2); + return NULL; + } + l2->ifindex = ifr.ifr_ifindex; + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_protocol = htons(protocol); + if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("bind[PF_PACKET]"); + close(l2->fd); + os_free(l2); + return NULL; + } + + if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { + perror("ioctl[SIOCGIFHWADDR]"); + close(l2->fd); + os_free(l2); + return NULL; + } + os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + int s; + struct ifreq ifr; + struct sockaddr_in *saddr; + size_t res; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl[SIOCGIFADDR]"); + close(s); + return -1; + } + close(s); + saddr = (struct sockaddr_in *) &ifr.ifr_addr; + if (saddr->sin_family != AF_INET) + return -1; + res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); + if (res >= len) + return -1; + return 0; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c new file mode 100644 index 0000000000000..7de58808d63c5 --- /dev/null +++ b/src/l2_packet/l2_packet_ndis.c @@ -0,0 +1,516 @@ +/* + * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This implementation requires Windows specific event loop implementation, + * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with + * driver_ndis.c, so only that driver interface can be used and + * CONFIG_USE_NDISUIO must be defined. + * + * WinXP version of the code uses overlapped I/O and a single threaded design + * with callback functions from I/O code. WinCE version uses a separate RX + * thread that blocks on ReadFile() whenever the media status is connected. + */ + +#include "includes.h" +#include <winsock2.h> +#include <ntddndis.h> + +#ifdef _WIN32_WCE +#include <winioctl.h> +#include <nuiouser.h> +#endif /* _WIN32_WCE */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + +#ifndef _WIN32_WCE +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) +#define IOCTL_NDISUIO_SET_ETHER_TYPE \ + _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#endif /* _WIN32_WCE */ + +/* From driver_ndis.c to shared the handle to NDISUIO */ +HANDLE driver_ndis_get_ndisuio_handle(void); + +/* + * NDISUIO supports filtering of only one ethertype at the time, so we must + * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth + * whenever wpa_supplicant is trying to pre-authenticate and then switching + * back to EAPOL when pre-authentication has been completed. + */ + +struct l2_packet_data; + +struct l2_packet_ndisuio_global { + int refcount; + unsigned short first_proto; + struct l2_packet_data *l2[2]; +#ifdef _WIN32_WCE + HANDLE rx_thread; + HANDLE stop_request; + HANDLE ready_for_read; + HANDLE rx_processed; +#endif /* _WIN32_WCE */ +}; + +static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL; + +struct l2_packet_data { + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + HANDLE rx_avail; +#ifndef _WIN32_WCE + OVERLAPPED rx_overlapped; +#endif /* _WIN32_WCE */ + u8 rx_buf[1514]; + DWORD rx_written; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + BOOL res; + DWORD written; + struct l2_ethhdr *eth; +#ifndef _WIN32_WCE + OVERLAPPED overlapped; +#endif /* _WIN32_WCE */ + OVERLAPPED *o; + + if (l2 == NULL) + return -1; + +#ifdef _WIN32_WCE + o = NULL; +#else /* _WIN32_WCE */ + os_memset(&overlapped, 0, sizeof(overlapped)); + o = &overlapped; +#endif /* _WIN32_WCE */ + + if (l2->l2_hdr) { + res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len, + &written, o); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen, + &written, o); + os_free(eth); + } + + if (!res) { + DWORD err = GetLastError(); +#ifndef _WIN32_WCE + if (err == ERROR_IO_PENDING) { + /* For now, just assume that the packet will be sent in + * time before the next write happens. This could be + * cleaned up at some point to actually wait for + * completion before starting new writes. + */ + return 0; + } +#endif /* _WIN32_WCE */ + wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +static void l2_packet_callback(struct l2_packet_data *l2); + +#ifdef _WIN32_WCE +static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2) +{ + HANDLE handles[2]; + + wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile"); + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, NULL)) { + DWORD err = GetLastError(); + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: " + "%d", (int) err); + /* + * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED + * error whenever the connection is not up. Yield the thread to + * avoid triggering a busy loop. Connection event should stop + * us from looping for long, but we need to allow enough CPU + * for the main thread to process the media disconnection. + */ + Sleep(100); + return; + } + + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet", + (int) l2->rx_written); + + /* + * Notify the main thread about the availability of a frame and wait + * for the frame to be processed. + */ + SetEvent(l2->rx_avail); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->rx_processed; + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ResetEvent(l2_ndisuio_global->rx_processed); +} + + +static DWORD WINAPI l2_packet_rx_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + DWORD res; + HANDLE handles[2]; + int run = 1; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started"); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->ready_for_read; + + /* + * Unfortunately, NDISUIO on WinCE does not seem to support waiting + * on the handle. There do not seem to be anything else that we could + * wait for either. If one were to modify NDISUIO to set a named event + * whenever packets are available, this event could be used here to + * avoid having to poll for new packets or we could even move to use a + * single threaded design. + * + * In addition, NDISUIO on WinCE is returning + * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while + * the adapter is not in connected state. For now, we are just using a + * local event to allow ReadFile calls only after having received NDIS + * media connect event. This event could be easily converted to handle + * another event if the protocol driver is replaced with somewhat more + * useful design. + */ + + while (l2_ndisuio_global && run) { + res = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + switch (res) { + case WAIT_OBJECT_0: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received " + "request to stop RX thread"); + run = 0; + break; + case WAIT_OBJECT_0 + 1: + l2_packet_rx_thread_try_read(l2); + break; + case WAIT_FAILED: + default: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: " + "WaitForMultipleObjects failed: %d", + (int) GetLastError()); + run = 0; + break; + } + } + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped"); + + return 0; +} +#else /* _WIN32_WCE */ +static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive) +{ + os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped)); + l2->rx_overlapped.hEvent = l2->rx_avail; + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped)) + { + DWORD err = GetLastError(); + if (err != ERROR_IO_PENDING) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: " + "%d", (int) err); + return -1; + } + /* + * Once read is completed, l2_packet_rx_event() will be + * called. + */ + } else { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data " + "without wait for completion"); + if (!recursive) + l2_packet_callback(l2); + } + + return 0; +} +#endif /* _WIN32_WCE */ + + +static void l2_packet_callback(struct l2_packet_data *l2) +{ + const u8 *rx_buf, *rx_src; + size_t rx_len; + struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes", + (int) l2->rx_written); + + if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) { + rx_buf = (u8 *) ethhdr; + rx_len = l2->rx_written; + } else { + rx_buf = (u8 *) (ethhdr + 1); + rx_len = l2->rx_written - sizeof(*ethhdr); + } + rx_src = ethhdr->h_source; + + l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len); +#ifndef _WIN32_WCE + l2_ndisuio_start_read(l2, 1); +#endif /* _WIN32_WCE */ +} + + +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + + if (l2_ndisuio_global) + l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1]; + + ResetEvent(l2->rx_avail); + +#ifndef _WIN32_WCE + if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(), + &l2->rx_overlapped, &l2->rx_written, FALSE)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult " + "failed: %d", (int) GetLastError()); + return; + } +#endif /* _WIN32_WCE */ + + l2_packet_callback(l2); + +#ifdef _WIN32_WCE + SetEvent(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ +} + + +static int l2_ndisuio_set_ether_type(unsigned short protocol) +{ + USHORT proto = htons(protocol); + DWORD written; + + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_NDISUIO_SET_ETHER_TYPE, &proto, + sizeof(proto), NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): " + "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + if (l2_ndisuio_global == NULL) { + l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global)); + if (l2_ndisuio_global == NULL) + return NULL; + l2_ndisuio_global->first_proto = protocol; + } + if (l2_ndisuio_global->refcount >= 2) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two " + "simultaneous connections allowed"); + return NULL; + } + l2_ndisuio_global->refcount++; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2; + + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_ndisuio_set_ether_type(protocol) < 0) { + os_free(l2); + return NULL; + } + + if (l2_ndisuio_global->refcount > 1) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting " + "filtering ethertype to %04x", protocol); + if (l2_ndisuio_global->l2[0]) + l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail; + return l2; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL) { + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + +#ifdef _WIN32_WCE + l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL); + /* + * This event is being set based on media connect/disconnect + * notifications in driver_ndis.c. + */ + l2_ndisuio_global->ready_for_read = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2_ndisuio_global->stop_request == NULL || + l2_ndisuio_global->ready_for_read == NULL || + l2_ndisuio_global->rx_processed == NULL) { + if (l2_ndisuio_global->stop_request) { + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + } + if (l2_ndisuio_global->ready_for_read) { + CloseHandle(l2_ndisuio_global->ready_for_read); + l2_ndisuio_global->ready_for_read = NULL; + } + if (l2_ndisuio_global->rx_processed) { + CloseHandle(l2_ndisuio_global->rx_processed); + l2_ndisuio_global->rx_processed = NULL; + } + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + os_free(l2); + return NULL; + } + + l2_ndisuio_global->rx_thread = CreateThread(NULL, 0, + l2_packet_rx_thread, l2, 0, + NULL); + if (l2_ndisuio_global->rx_thread == NULL) { + wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX " + "thread: %d", (int) GetLastError()); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + os_free(l2); + return NULL; + } +#else /* _WIN32_WCE */ + l2_ndisuio_start_read(l2, 0); +#endif /* _WIN32_WCE */ + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2_ndisuio_global) { + l2_ndisuio_global->refcount--; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL; + if (l2_ndisuio_global->refcount) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering " + "ethertype to %04x", + l2_ndisuio_global->first_proto); + l2_ndisuio_set_ether_type( + l2_ndisuio_global->first_proto); + return; + } + +#ifdef _WIN32_WCE + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to " + "stop"); + SetEvent(l2_ndisuio_global->stop_request); + /* + * Cancel pending ReadFile() in the RX thread (if we were still + * connected at this point). + */ + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL, + NULL)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ " + "failed: %d", (int) GetLastError()); + /* RX thread will exit blocking ReadFile once NDISUIO + * notices that the adapter is disconnected. */ + } + WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE); + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited"); + CloseHandle(l2_ndisuio_global->rx_thread); + CloseHandle(l2_ndisuio_global->stop_request); + CloseHandle(l2_ndisuio_global->ready_for_read); + CloseHandle(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ + + os_free(l2_ndisuio_global); + l2_ndisuio_global = NULL; + } + +#ifndef _WIN32_WCE + CancelIo(driver_ndis_get_ndisuio_handle()); +#endif /* _WIN32_WCE */ + + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/contrib/hostapd/l2_packet_none.c b/src/l2_packet/l2_packet_none.c index 2add5b7caa431..5e3f6e972384f 100644 --- a/contrib/hostapd/l2_packet_none.c +++ b/src/l2_packet/l2_packet_none.c @@ -81,7 +81,7 @@ struct l2_packet_data * l2_packet_init( l2 = os_zalloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; - os_strncpy(l2->ifname, ifname, sizeof(l2->ifname)); + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c new file mode 100644 index 0000000000000..8156e294b1bb2 --- /dev/null +++ b/src/l2_packet/l2_packet_pcap.c @@ -0,0 +1,386 @@ +/* + * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/ioctl.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <pcap.h> +#ifndef CONFIG_WINPCAP +#include <dnet.h> +#endif /* CONFIG_WINPCAP */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; +#ifdef CONFIG_WINPCAP + unsigned int num_fast_poll; +#else /* CONFIG_WINPCAP */ + eth_t *eth; +#endif /* CONFIG_WINPCAP */ + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls + * to rx_callback */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +#ifndef CONFIG_WINPCAP +static int l2_packet_init_libdnet(struct l2_packet_data *l2) +{ + eth_addr_t own_addr; + + l2->eth = eth_open(l2->ifname); + if (!l2->eth) { + printf("Failed to open interface '%s'.\n", l2->ifname); + perror("eth_open"); + return -1; + } + + if (eth_get(l2->eth, &own_addr) < 0) { + printf("Failed to get own hw address from interface '%s'.\n", + l2->ifname); + perror("eth_get"); + eth_close(l2->eth); + l2->eth = NULL; + return -1; + } + os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN); + + return 0; +} +#endif /* CONFIG_WINPCAP */ + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, buf, len); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, buf, len); +#endif /* CONFIG_WINPCAP */ + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, (u8 *) eth, mlen); +#endif /* CONFIG_WINPCAP */ + + os_free(eth); + } + + return ret; +} + + +#ifndef CONFIG_WINPCAP +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} +#endif /* CONFIG_WINPCAP */ + + +#ifdef CONFIG_WINPCAP +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr->caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; +} + + +static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = timeout_ctx; + int timeout; + + if (l2->num_fast_poll > 0) { + timeout = 20000; + l2->num_fast_poll--; + } else + timeout = 100000; + + /* Register new timeout before calling l2_packet_receive() since + * receive handler may free this l2_packet instance (which will + * cancel this timeout). */ + eloop_register_timeout(0, timeout, l2_packet_receive_timeout, + l2, pcap); + pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2); +} +#endif /* CONFIG_WINPCAP */ + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + +#ifdef CONFIG_WINPCAP + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname); + pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", ifname); + return -1; + } + if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0) + fprintf(stderr, "pcap_setnonblock: %s\n", + pcap_geterr(l2->pcap)); +#else /* CONFIG_WINPCAP */ + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } +#endif /* CONFIG_WINPCAP */ + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); +#ifdef BIOCIMMEDIATE + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } +#endif /* BIOCIMMEDIATE */ + +#ifdef CONFIG_WINPCAP + eloop_register_timeout(0, 100000, l2_packet_receive_timeout, + l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + +#ifdef CONFIG_WINPCAP + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); +#else /* CONFIG_WINPCAP */ + if (l2_packet_init_libdnet(l2)) + return NULL; +#endif /* CONFIG_WINPCAP */ + + if (l2_packet_init_libpcap(l2, protocol)) { +#ifndef CONFIG_WINPCAP + eth_close(l2->eth); +#endif /* CONFIG_WINPCAP */ + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + +#ifdef CONFIG_WINPCAP + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + if (l2->eth) + eth_close(l2->eth); + eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap)); +#endif /* CONFIG_WINPCAP */ + if (l2->pcap) + pcap_close(l2->pcap); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +#ifdef CONFIG_WINPCAP + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); + eloop_register_timeout(0, 10000, l2_packet_receive_timeout, + l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ +} diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c new file mode 100644 index 0000000000000..c0e7c495a142a --- /dev/null +++ b/src/l2_packet/l2_packet_privsep.c @@ -0,0 +1,267 @@ +/* + * WPA Supplicant - Layer2 packet handling with privilege separation + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" +#include "privsep_commands.h" + + +struct l2_packet_data { + int fd; /* UNIX domain socket for privsep access */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + u8 own_addr[ETH_ALEN]; + char *own_socket_path; + struct sockaddr_un priv_addr; +}; + + +static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, + const void *data, size_t data_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(cmd)"); + return -1; + } + + return 0; +} + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + struct msghdr msg; + struct iovec io[4]; + int cmd = PRIVSEP_CMD_L2_SEND; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = &dst_addr; + io[1].iov_len = ETH_ALEN; + io[2].iov_base = &proto; + io[2].iov_len = 2; + io[3].iov_base = (u8 *) buf; + io[3].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 4; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(packet_send)"); + return -1; + } + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + os_memset(&from, 0, sizeof(from)); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("l2_packet_receive - recvfrom"); + return; + } + if (res < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Too show packet received"); + return; + } + + if (from.sun_family != AF_UNIX || + os_strncmp(from.sun_path, l2->priv_addr.sun_path, + sizeof(from.sun_path)) != 0) { + wpa_printf(MSG_DEBUG, "L2: Received message from unexpected " + "source"); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN, + res - ETH_ALEN); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + char *own_dir = "/tmp"; + char *priv_dir = "/var/run/wpa_priv"; + size_t len; + static unsigned int counter = 0; + struct sockaddr_un addr; + fd_set rfds; + struct timeval tv; + int res; + u8 reply[ETH_ALEN + 1]; + int reg_cmd[2]; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + + len = os_strlen(own_dir) + 50; + l2->own_socket_path = os_malloc(len); + if (l2->own_socket_path == NULL) { + os_free(l2); + return NULL; + } + os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d", + own_dir, getpid(), counter++); + + l2->priv_addr.sun_family = AF_UNIX; + os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path), + "%s/%s", priv_dir, ifname); + + l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (l2->fd < 0) { + perror("socket(PF_UNIX)"); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); + if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + + reg_cmd[0] = protocol; + reg_cmd[1] = l2_hdr; + if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd)) + < 0) { + wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv"); + goto fail; + } + + FD_ZERO(&rfds); + FD_SET(l2->fd, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + goto fail; + } + + if (FD_ISSET(l2->fd, &rfds)) { + res = recv(l2->fd, reply, sizeof(reply), 0); + if (res < 0) { + perror("recv"); + goto fail; + } + } else { + wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for " + "registration reply"); + goto fail; + } + + if (res != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply " + "(len=%d)", res); + } + os_memcpy(l2->own_addr, reply, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; + +fail: + close(l2->fd); + l2->fd = -1; + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0); + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + if (l2->own_socket_path) { + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); +} diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c new file mode 100644 index 0000000000000..f76b386fc033a --- /dev/null +++ b/src/l2_packet/l2_packet_winpcap.c @@ -0,0 +1,341 @@ +/* + * WPA Supplicant - Layer2 packet handling with WinPcap RX thread + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This l2_packet implementation is explicitly for WinPcap and Windows events. + * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive + * frames which means relatively long latency for EAPOL RX processing. The + * implementation here uses a separate thread to allow WinPcap to be receiving + * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms + * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms + * is added in to receive thread whenever no EAPOL frames has been received for + * a while. Whenever an EAPOL handshake is expected, this sleep is removed. + * + * The RX thread receives a frame and signals main thread through Windows event + * about the availability of a new frame. Processing the received frame is + * synchronized with pair of Windows events so that no extra buffer or queuing + * mechanism is needed. This implementation requires Windows specific event + * loop implementation, i.e., eloop_win.c. + * + * WinPcap has pcap_getevent() that could, in theory at least, be used to + * implement this kind of waiting with a simpler single-thread design. However, + * that event handle is not really signaled immediately when receiving each + * frame, so it does not really work for this kind of use. + */ + +#include "includes.h" +#include <pcap.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +/* + * Number of pcap_dispatch() iterations to do without extra wait after each + * received EAPOL packet or authentication notification. This is used to reduce + * latency for EAPOL receive. + */ +static const size_t no_wait_count = 750; + +struct l2_packet_data { + pcap_t *pcap; + unsigned int num_fast_poll; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + int running; + HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify; + u8 *rx_buf, *rx_src; + size_t rx_len; + size_t rx_no_wait; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { + ret = pcap_sendpacket(l2->pcap, buf, len); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); + os_free(eth); + } + + return ret; +} + + +/* pcap_dispatch() callback for the RX thread */ +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + l2->rx_buf = (u8 *) ethhdr; + l2->rx_len = hdr->caplen; + } else { + l2->rx_buf = (u8 *) (ethhdr + 1); + l2->rx_len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_src = ethhdr->h_source; + SetEvent(l2->rx_avail); + WaitForSingleObject(l2->rx_done, INFINITE); + ResetEvent(l2->rx_done); + l2->rx_no_wait = no_wait_count; +} + + +/* main RX loop that is running in a separate thread */ +static DWORD WINAPI l2_packet_receive_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + + while (l2->running) { + pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb, + (u_char *) l2); + if (l2->rx_no_wait > 0) + l2->rx_no_wait--; + if (WaitForSingleObject(l2->rx_notify, + l2->rx_no_wait ? 0 : 50) == + WAIT_OBJECT_0) { + l2->rx_no_wait = no_wait_count; + ResetEvent(l2->rx_notify); + } + } + SetEvent(l2->rx_thread_done); + ExitThread(0); + return 0; +} + + +/* main thread RX event handler */ +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf, + l2->rx_len); + ResetEvent(l2->rx_avail); + SetEvent(l2->rx_done); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + DWORD thread_id; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + else + os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s", + ifname); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL || l2->rx_done == NULL || + l2->rx_notify == NULL) { + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + pcap_close(l2->pcap); + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + + l2->running = 1; + l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0, + &thread_id); + + return l2; +} + + +static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + + if (l2->rx_thread_done && + WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) { + wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not " + "exit - kill it\n"); + TerminateThread(l2->rx_thread, 0); + } + CloseHandle(l2->rx_thread_done); + CloseHandle(l2->rx_thread); + if (l2->pcap) + pcap_close(l2->pcap); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + os_free(l2); +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL); + + l2->running = 0; + pcap_breakloop(l2->pcap); + + /* + * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done + * event and this event is set in l2_packet_rx_event(). However, + * l2_packet_deinit() may end up being called from l2->rx_callback(), + * so we need to return from here and complete deinitialization in + * a registered timeout to avoid having to forcefully kill the RX + * thread. + */ + eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + if (l2) + SetEvent(l2->rx_notify); +} diff --git a/src/radius/.gitignore b/src/radius/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/radius/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/radius/Makefile b/src/radius/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/radius/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/hostapd/radius.c b/src/radius/radius.c index 743f3400f1de7..71bbfb52ee7ce 100644 --- a/contrib/hostapd/radius.c +++ b/src/radius/radius.c @@ -20,6 +20,13 @@ #include "crypto.h" +static struct radius_attr_hdr * +radius_get_attr_hdr(struct radius_msg *msg, int idx) +{ + return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]); +} + + struct radius_msg *radius_msg_new(u8 code, u8 identifier) { struct radius_msg *msg; @@ -45,7 +52,7 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len) return -1; os_memset(msg, 0, sizeof(*msg)); - msg->buf = wpa_zalloc(init_len); + msg->buf = os_zalloc(init_len); if (msg->buf == NULL) return -1; @@ -53,9 +60,9 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len) msg->hdr = (struct radius_hdr *) msg->buf; msg->buf_used = sizeof(*msg->hdr); - msg->attrs = - os_malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); - if (msg->attrs == NULL) { + msg->attr_pos = + os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); + if (msg->attr_pos == NULL) { os_free(msg->buf); msg->buf = NULL; msg->hdr = NULL; @@ -78,17 +85,13 @@ void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) void radius_msg_free(struct radius_msg *msg) { - if (msg->buf != NULL) { - os_free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; - } + os_free(msg->buf); + msg->buf = NULL; + msg->hdr = NULL; msg->buf_size = msg->buf_used = 0; - if (msg->attrs != NULL) { - os_free(msg->attrs); - msg->attrs = NULL; - } + os_free(msg->attr_pos); + msg->attr_pos = NULL; msg->attr_size = msg->attr_used = 0; } @@ -126,6 +129,7 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, @@ -177,6 +181,8 @@ static struct radius_attr_type radius_attrs[] = RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", + RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, }; #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) @@ -281,12 +287,14 @@ void radius_msg_dump(struct radius_msg *msg) msg->hdr->identifier, ntohs(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { - radius_msg_dump_attr(msg->attrs[i]); + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + radius_msg_dump_attr(attr); } } -int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len) +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len) { if (secret) { u8 auth[MD5_MAC_LEN]; @@ -357,7 +365,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, } -void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[2]; @@ -382,18 +390,19 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr) { if (msg->attr_used >= msg->attr_size) { - struct radius_attr_hdr **nattrs; + size_t *nattr_pos; int nlen = msg->attr_size * 2; - nattrs = os_realloc(msg->attrs, nlen * sizeof(*msg->attrs)); - if (nattrs == NULL) + nattr_pos = os_realloc(msg->attr_pos, + nlen * sizeof(*msg->attr_pos)); + if (nattr_pos == NULL) return -1; - msg->attrs = nattrs; + msg->attr_pos = nattr_pos; msg->attr_size = nlen; } - msg->attrs[msg->attr_used++] = attr; + msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf; return 0; } @@ -416,21 +425,15 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, if (msg->buf_size < buf_needed) { /* allocate more space for message buffer */ unsigned char *nbuf; - size_t i, nlen = msg->buf_size; - int diff; + size_t nlen = msg->buf_size; while (nlen < buf_needed) nlen *= 2; nbuf = os_realloc(msg->buf, nlen); if (nbuf == NULL) return NULL; - diff = nbuf - msg->buf; msg->buf = nbuf; msg->hdr = (struct radius_hdr *) msg->buf; - /* adjust attr pointers to match with the new buffer */ - for (i = 0; i < msg->attr_used; i++) - msg->attrs[i] = (struct radius_attr_hdr *) - (((u8 *) msg->attrs[i]) + diff); os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); msg->buf_size = nlen; } @@ -543,15 +546,16 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) { u8 *eap, *pos; size_t len, i; + struct radius_attr_hdr *attr; if (msg == NULL) return NULL; len = 0; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) - len += msg->attrs[i]->length - - sizeof(struct radius_attr_hdr); + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) @@ -563,8 +567,8 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) pos = eap; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) { - struct radius_attr_hdr *attr = msg->attrs[i]; + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { int flen = attr->length - sizeof(*attr); os_memcpy(pos, attr + 1, flen); pos += flen; @@ -583,17 +587,18 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, { u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; u8 orig_authenticator[16]; - struct radius_attr_hdr *attr = NULL; + struct radius_attr_hdr *attr = NULL, *tmp; size_t i; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { printf("Multiple Message-Authenticator " "attributes in RADIUS message\n"); return 1; } - attr = msg->attrs[i]; + attr = tmp; } } @@ -671,7 +676,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, int count = 0; for (i = 0; i < src->attr_used; i++) { - attr = src->attrs[i]; + attr = radius_get_attr_hdr(src, i); if (attr->type == type) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) @@ -724,7 +729,7 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, return NULL; for (i = 0; i < msg->attr_used; i++) { - struct radius_attr_hdr *attr = msg->attrs[i]; + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); size_t left; u32 vendor_id; struct radius_attr_vendor *vhdr; @@ -801,6 +806,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, ppos = plain = os_malloc(plen); if (plain == NULL) return NULL; + plain[0] = 0; while (left > 0) { /* b(1) = MD5(Secret + Request-Authenticator + Salt) @@ -825,7 +831,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, left -= MD5_MAC_LEN; } - if (plain[0] > plen - 1) { + if (plain[0] == 0 || plain[0] > plen - 1) { printf("Failed to decrypt MPPE key\n"); os_free(plain); return NULL; @@ -854,8 +860,7 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, const u8 *addr[3]; size_t _len[3]; - saltbuf[0] = salt >> 8; - saltbuf[1] = salt; + WPA_PUT_BE16(saltbuf, salt); len = 1 + key_len; if (len & 0x0f) { @@ -895,7 +900,7 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len) + const u8 *secret, size_t secret_len) { u8 *key; size_t keylen; @@ -904,7 +909,7 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, if (msg == NULL || sent_msg == NULL) return NULL; - keys = wpa_zalloc(sizeof(*keys)); + keys = os_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; @@ -936,7 +941,7 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, struct radius_ms_mppe_keys * radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len) + const u8 *secret, size_t secret_len) { u8 *key; size_t keylen; @@ -945,7 +950,7 @@ radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, if (msg == NULL || sent_msg == NULL) return NULL; - keys = wpa_zalloc(sizeof(*keys)); + keys = os_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; @@ -993,8 +998,8 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; pos = (u8 *) (vhdr + 1); salt = os_random() | 0x8000; - *pos++ = salt >> 8; - *pos++ = salt; + WPA_PUT_BE16(pos, salt); + pos += 2; encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, secret_len, pos, &elen); vhdr->vendor_length = hlen + elen - sizeof(vendor_id); @@ -1018,8 +1023,8 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; pos = (u8 *) (vhdr + 1); salt ^= 1; - *pos++ = salt >> 8; - *pos++ = salt; + WPA_PUT_BE16(pos, salt); + pos += 2; encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, secret_len, pos, &elen); vhdr->vendor_length = hlen + elen - sizeof(vendor_id); @@ -1039,8 +1044,8 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, * in RFC 2865, Chap. 5.2 */ struct radius_attr_hdr * radius_msg_add_attr_user_password(struct radius_msg *msg, - u8 *data, size_t data_len, - u8 *secret, size_t secret_len) + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len) { u8 buf[128]; int padlen, i; @@ -1092,12 +1097,13 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) { - struct radius_attr_hdr *attr = NULL; + struct radius_attr_hdr *attr = NULL, *tmp; size_t i, dlen; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type) { - attr = msg->attrs[i]; + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type) { + attr = tmp; break; } } @@ -1116,12 +1122,13 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start) { size_t i; - struct radius_attr_hdr *attr = NULL; + struct radius_attr_hdr *attr = NULL, *tmp; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type && - (start == NULL || (u8 *) msg->attrs[i] > start)) { - attr = msg->attrs[i]; + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type && + (start == NULL || (u8 *) tmp > start)) { + attr = tmp; break; } } @@ -1141,9 +1148,9 @@ int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) int 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) + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + if (attr->type == type && + attr->length >= sizeof(struct radius_attr_hdr) + min_len) count++; } @@ -1176,7 +1183,7 @@ int radius_msg_get_vlanid(struct radius_msg *msg) os_memset(&tunnel, 0, sizeof(tunnel)); for (i = 0; i < msg->attr_used; i++) { - attr = msg->attrs[i]; + attr = radius_get_attr_hdr(msg, i); data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) @@ -1191,14 +1198,13 @@ int radius_msg_get_vlanid(struct radius_msg *msg) if (attr->length != 6) break; tun->tag_used++; - tun->type = (data[1] << 16) | (data[2] << 8) | data[3]; + tun->type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: if (attr->length != 6) break; tun->tag_used++; - tun->medium_type = - (data[1] << 16) | (data[2] << 8) | data[3]; + tun->medium_type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: if (data[0] < RADIUS_TUNNEL_TAGS) { diff --git a/contrib/hostapd/radius.h b/src/radius/radius.h index d4375376a94f4..c30621dc2eaf3 100644 --- a/contrib/hostapd/radius.h +++ b/src/radius/radius.h @@ -1,6 +1,6 @@ /* * hostapd / RADIUS message processing - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -53,6 +53,7 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_NAS_IP_ADDRESS = 4, RADIUS_ATTR_NAS_PORT = 5, RADIUS_ATTR_FRAMED_MTU = 12, + RADIUS_ATTR_REPLY_MESSAGE = 18, RADIUS_ATTR_STATE = 24, RADIUS_ATTR_CLASS = 25, RADIUS_ATTR_VENDOR_SPECIFIC = 26, @@ -86,6 +87,7 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 }; @@ -179,7 +181,9 @@ struct radius_msg { struct radius_hdr *hdr; - struct radius_attr_hdr **attrs; /* array of pointers to attributes */ + size_t *attr_pos; /* array of indexes to attributes (number of bytes + * from buf to the beginning of + * struct radius_attr_hdr). */ size_t attr_size; /* total size of the attribute pointer array */ size_t attr_used; /* total number of attributes in the array */ }; @@ -203,10 +207,11 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len); void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); void radius_msg_free(struct radius_msg *msg); void radius_msg_dump(struct radius_msg *msg); -int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len); +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len); int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator); -void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); @@ -225,10 +230,10 @@ void radius_msg_make_authenticator(struct radius_msg *msg, const u8 *data, size_t len); struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len); + const u8 *secret, size_t secret_len); struct radius_ms_mppe_keys * radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len); + const u8 *secret, size_t secret_len); int radius_msg_add_mppe_keys(struct radius_msg *msg, const u8 *req_authenticator, const u8 *secret, size_t secret_len, @@ -236,8 +241,8 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, const u8 *recv_key, size_t recv_key_len); struct radius_attr_hdr * radius_msg_add_attr_user_password(struct radius_msg *msg, - u8 *data, size_t data_len, - u8 *secret, size_t secret_len); + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); int radius_msg_get_vlanid(struct radius_msg *msg); diff --git a/contrib/hostapd/radius_client.c b/src/radius/radius_client.c index 81cd9c58b16bf..673e97eb7560a 100644 --- a/contrib/hostapd/radius_client.c +++ b/src/radius/radius_client.c @@ -14,7 +14,7 @@ #include "includes.h" -#include "hostapd.h" +#include "common.h" #include "radius.h" #include "radius_client.h" #include "eloop.h" @@ -35,7 +35,8 @@ struct radius_rx_handler { RadiusRxResult (*handler)(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, + const u8 *shared_secret, + size_t shared_secret_len, void *data); void *data; }; @@ -106,7 +107,7 @@ 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, + const u8 *shared_secret, size_t shared_secret_len, void *data), void *data) @@ -363,7 +364,7 @@ static void radius_client_list_add(struct radius_client_data *radius, return; } - entry = wpa_zalloc(sizeof(*entry)); + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { printf("Failed to add RADIUS packet into retransmit list\n"); radius_msg_free(msg); @@ -712,9 +713,9 @@ void radius_client_flush(struct radius_client_data *radius, int only_auth) } -void radius_client_update_acct_msgs(struct radius_client_data *radius, - u8 *shared_secret, - size_t shared_secret_len) +static void radius_client_update_acct_msgs(struct radius_client_data *radius, + u8 *shared_secret, + size_t shared_secret_len) { struct radius_msg_list *entry; @@ -738,15 +739,16 @@ radius_change_server(struct radius_client_data *radius, struct hostapd_radius_server *oserv, int sock, int sock6, int auth) { - struct sockaddr_in serv; + struct sockaddr_in serv, claddr; #ifdef CONFIG_IPV6 - struct sockaddr_in6 serv6; + struct sockaddr_in6 serv6, claddr6; #endif /* CONFIG_IPV6 */ - struct sockaddr *addr; - socklen_t addrlen; + struct sockaddr *addr, *cl_addr; + socklen_t addrlen, claddrlen; char abuf[50]; int sel_sock; struct radius_msg_list *entry; + struct hostapd_radius_servers *conf = radius->conf; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -816,11 +818,65 @@ radius_change_server(struct radius_client_data *radius, return -1; } + if (conf->force_client_addr) { + switch (conf->client_addr.af) { + case AF_INET: + os_memset(&claddr, 0, sizeof(claddr)); + claddr.sin_family = AF_INET; + claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; + claddr.sin_port = htons(0); + cl_addr = (struct sockaddr *) &claddr; + claddrlen = sizeof(claddr); + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + os_memset(&claddr6, 0, sizeof(claddr6)); + claddr6.sin6_family = AF_INET6; + os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, + sizeof(struct in6_addr)); + claddr6.sin6_port = htons(0); + cl_addr = (struct sockaddr *) &claddr6; + claddrlen = sizeof(claddr6); + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } + + if (bind(sel_sock, cl_addr, claddrlen) < 0) { + perror("bind[radius]"); + return -1; + } + } + if (connect(sel_sock, addr, addrlen) < 0) { perror("connect[radius]"); return -1; } +#ifndef CONFIG_NATIVE_WINDOWS + switch (nserv->addr.af) { + case AF_INET: + claddrlen = sizeof(claddr); + getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); + break; +#ifdef CONFIG_IPV6 + case AF_INET6: { + claddrlen = sizeof(claddr6); + getsockname(sel_sock, (struct sockaddr *) &claddr6, + &claddrlen); + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntop(AF_INET6, &claddr6.sin6_addr, + abuf, sizeof(abuf)), + ntohs(claddr6.sin6_port)); + break; + } +#endif /* CONFIG_IPV6 */ + } +#endif /* CONFIG_NATIVE_WINDOWS */ + if (auth) radius->auth_sock = sel_sock; else @@ -966,7 +1022,7 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { struct radius_client_data *radius; - radius = wpa_zalloc(sizeof(struct radius_client_data)); + radius = os_zalloc(sizeof(struct radius_client_data)); if (radius == NULL) return NULL; @@ -1184,8 +1240,8 @@ static int radius_servers_diff(struct hostapd_radius_server *nserv, if (hostapd_ip_diff(&nserv[i].addr, &oserv[i].addr) || nserv[i].port != oserv[i].port || nserv[i].shared_secret_len != oserv[i].shared_secret_len || - memcmp(nserv[i].shared_secret, oserv[i].shared_secret, - nserv[i].shared_secret_len) != 0) + os_memcmp(nserv[i].shared_secret, oserv[i].shared_secret, + nserv[i].shared_secret_len) != 0) return 1; } diff --git a/contrib/hostapd/radius_client.h b/src/radius/radius_client.h index df1ba1f9b209f..4fe9ba9a42c76 100644 --- a/contrib/hostapd/radius_client.h +++ b/src/radius/radius_client.h @@ -15,7 +15,7 @@ #ifndef RADIUS_CLIENT_H #define RADIUS_CLIENT_H -#include "config_types.h" +#include "ip_addr.h" struct radius_msg; @@ -57,6 +57,9 @@ struct hostapd_radius_servers { int acct_interim_interval; int msg_dumps; + + struct hostapd_ip_addr client_addr; + int force_client_addr; }; @@ -82,7 +85,7 @@ 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, + const u8 *shared_secret, size_t shared_secret_len, void *data), void *data); int radius_client_send(struct radius_client_data *radius, diff --git a/contrib/hostapd/radius_server.c b/src/radius/radius_server.c index 409d537f42ab2..1bfb93c70bccb 100644 --- a/contrib/hostapd/radius_server.c +++ b/src/radius/radius_server.c @@ -18,8 +18,8 @@ #include "common.h" #include "radius.h" #include "eloop.h" -#include "config.h" -#include "eap.h" +#include "defs.h" +#include "eap_server/eap.h" #include "radius_server.h" #define RADIUS_SESSION_TIMEOUT 60 @@ -50,10 +50,7 @@ struct radius_session { struct radius_server_data *server; unsigned int sess_id; struct eap_sm *eap; - u8 *eapKeyData, *eapReqData; - size_t eapKeyDataLen, eapReqDataLen; - Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq; - Boolean portEnabled, eapTimeout; + struct eap_eapol_interface *eap_if; struct radius_msg *last_msg; char *last_from_addr; @@ -83,13 +80,27 @@ struct radius_server_data { int auth_sock; struct radius_client *clients; unsigned int next_sess_id; - void *hostapd_conf; + void *conf_ctx; int num_sess; void *eap_sim_db_priv; void *ssl_ctx; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; int ipv6; struct os_time start_time; struct radius_server_counters counters; + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + char *eap_req_id_text; + size_t eap_req_id_text_len; }; @@ -168,19 +179,17 @@ static void radius_server_session_free(struct radius_server_data *data, struct radius_session *sess) { eloop_cancel_timeout(radius_server_session_timeout, data, sess); - free(sess->eapKeyData); - free(sess->eapReqData); - eap_sm_deinit(sess->eap); + eap_server_sm_deinit(sess->eap); if (sess->last_msg) { radius_msg_free(sess->last_msg); - free(sess->last_msg); + os_free(sess->last_msg); } - free(sess->last_from_addr); + os_free(sess->last_from_addr); if (sess->last_reply) { radius_msg_free(sess->last_reply); - free(sess->last_reply); + os_free(sess->last_reply); } - free(sess); + os_free(sess); data->num_sess--; } @@ -246,7 +255,7 @@ radius_server_new_session(struct radius_server_data *data, return NULL; } - sess = wpa_zalloc(sizeof(*sess)); + sess = os_zalloc(sizeof(*sess)); if (sess == NULL) return NULL; @@ -269,30 +278,29 @@ radius_server_get_new_session(struct radius_server_data *data, { u8 *user; size_t user_len; - const struct hostapd_eap_user *eap_user; int res; struct radius_session *sess; struct eap_config eap_conf; RADIUS_DEBUG("Creating a new session"); - user = malloc(256); + user = os_malloc(256); if (user == NULL) { return NULL; } res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); if (res < 0 || res > 256) { RADIUS_DEBUG("Could not get User-Name"); - free(user); + os_free(user); return NULL; } user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); - eap_user = hostapd_get_eap_user(data->hostapd_conf, user, user_len, 0); - free(user); + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); + os_free(user); - if (eap_user) { + if (res == 0) { RADIUS_DEBUG("Matching user entry found"); sess = radius_server_new_session(data, client); if (sess == NULL) { @@ -304,19 +312,32 @@ radius_server_get_new_session(struct radius_server_data *data, return NULL; } - memset(&eap_conf, 0, sizeof(eap_conf)); + os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; eap_conf.backend_auth = TRUE; - sess->eap = eap_sm_init(sess, &radius_server_eapol_cb, &eap_conf); + eap_conf.eap_server = 1; + eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key; + eap_conf.eap_fast_a_id = data->eap_fast_a_id; + eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info; + eap_conf.eap_fast_prov = data->eap_fast_prov; + eap_conf.pac_key_lifetime = data->pac_key_lifetime; + eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; + eap_conf.tnc = data->tnc; + eap_conf.wps = data->wps; + sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, + &eap_conf); if (sess->eap == NULL) { RADIUS_DEBUG("Failed to initialize EAP state machine for the " "new session"); radius_server_session_free(data, sess); return NULL; } - sess->eapRestart = TRUE; - sess->portEnabled = TRUE; + sess->eap_if = eap_get_interface(sess->eap); + sess->eap_if->eapRestart = TRUE; + sess->eap_if->portEnabled = TRUE; RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); @@ -334,11 +355,14 @@ radius_server_encapsulate_eap(struct radius_server_data *data, int code; unsigned int sess_id; - if (sess->eapFail) { + if (sess->eap_if->eapFail) { + sess->eap_if->eapFail = FALSE; code = RADIUS_CODE_ACCESS_REJECT; - } else if (sess->eapSuccess) { + } else if (sess->eap_if->eapSuccess) { + sess->eap_if->eapSuccess = FALSE; code = RADIUS_CODE_ACCESS_ACCEPT; } else { + sess->eap_if->eapReq = FALSE; code = RADIUS_CODE_ACCESS_CHALLENGE; } @@ -355,23 +379,25 @@ radius_server_encapsulate_eap(struct radius_server_data *data, RADIUS_DEBUG("Failed to add State attribute"); } - if (sess->eapReqData && - !radius_msg_add_eap(msg, sess->eapReqData, sess->eapReqDataLen)) { + if (sess->eap_if->eapReqData && + !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), + wpabuf_len(sess->eap_if->eapReqData))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); } - if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eapKeyData) { + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; - if (sess->eapKeyDataLen > 64) { + if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { - len = sess->eapKeyDataLen / 2; + len = sess->eap_if->eapKeyDataLen / 2; } if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, (u8 *) client->shared_secret, client->shared_secret_len, - sess->eapKeyData + len, len, - sess->eapKeyData, len)) { + sess->eap_if->eapKeyData + len, + len, sess->eap_if->eapKeyData, + len)) { RADIUS_DEBUG("Failed to add MPPE key attributes"); } } @@ -412,10 +438,10 @@ static int radius_server_reject(struct radius_server_data *data, return -1; } - memset(&eapfail, 0, sizeof(eapfail)); + os_memset(&eapfail, 0, sizeof(eapfail)); eapfail.code = EAP_CODE_FAILURE; eapfail.identifier = 0; - eapfail.length = htons(sizeof(eapfail)); + eapfail.length = host_to_be16(sizeof(eapfail)); if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); @@ -447,7 +473,7 @@ static int radius_server_reject(struct radius_server_data *data, } radius_msg_free(msg); - free(msg); + os_free(msg); return ret; } @@ -463,11 +489,10 @@ static int radius_server_request(struct radius_server_data *data, u8 *eap = NULL; size_t eap_len; int res, state_included = 0; - u8 statebuf[4], resp_id; + u8 statebuf[4]; unsigned int state; struct radius_session *sess; struct radius_msg *reply; - struct eap_hdr *hdr; if (force_sess) sess = force_sess; @@ -476,8 +501,7 @@ static int radius_server_request(struct radius_server_data *data, sizeof(statebuf)); state_included = res >= 0; if (res == sizeof(statebuf)) { - state = (statebuf[0] << 24) | (statebuf[1] << 16) | - (statebuf[2] << 8) | statebuf[3]; + state = WPA_GET_BE32(statebuf); sess = radius_server_get_session(client, state); } else { sess = NULL; @@ -534,12 +558,6 @@ static int radius_server_request(struct radius_server_data *data, } RADIUS_DUMP("Received EAP data", eap, eap_len); - if (eap_len >= sizeof(*hdr)) { - hdr = (struct eap_hdr *) eap; - resp_id = hdr->identifier; - } else { - resp_id = 0; - } /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. @@ -548,36 +566,33 @@ static int radius_server_request(struct radius_server_data *data, * If code is not 1-4, discard the packet silently. * Or is this already done by the EAP state machine? */ - eap_set_eapRespData(sess->eap, eap, eap_len); - free(eap); + wpabuf_free(sess->eap_if->eapRespData); + sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); + if (sess->eap_if->eapRespData == NULL) + os_free(eap); eap = NULL; - sess->eapResp = TRUE; - eap_sm_step(sess->eap); + sess->eap_if->eapResp = TRUE; + eap_server_sm_step(sess->eap); - if (sess->eapReqData) { + if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || + sess->eap_if->eapFail) && sess->eap_if->eapReqData) { RADIUS_DUMP("EAP data from the state machine", - sess->eapReqData, sess->eapReqDataLen); - } else if (sess->eapFail) { + wpabuf_head(sess->eap_if->eapReqData), + wpabuf_len(sess->eap_if->eapReqData)); + } else if (sess->eap_if->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " - "set - generate EAP-Failure"); - hdr = wpa_zalloc(sizeof(*hdr)); - if (hdr) { - hdr->identifier = resp_id; - hdr->length = htons(sizeof(*hdr)); - sess->eapReqData = (u8 *) hdr; - sess->eapReqDataLen = sizeof(*hdr); - } + "set"); } else if (eap_sm_method_pending(sess->eap)) { if (sess->last_msg) { radius_msg_free(sess->last_msg); - free(sess->last_msg); + os_free(sess->last_msg); } sess->last_msg = msg; sess->last_from_port = from_port; - free(sess->last_from_addr); - sess->last_from_addr = strdup(from_addr); + os_free(sess->last_from_addr); + sess->last_from_addr = os_strdup(from_addr); sess->last_fromlen = fromlen; - memcpy(&sess->last_from, from, fromlen); + os_memcpy(&sess->last_from, from, fromlen); return -2; } else { RADIUS_DEBUG("No EAP data from the state machine - ignore this" @@ -590,10 +605,6 @@ static int radius_server_request(struct radius_server_data *data, reply = radius_server_encapsulate_eap(data, client, sess, msg); - free(sess->eapReqData); - sess->eapReqData = NULL; - sess->eapReqDataLen = 0; - if (reply) { RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { @@ -621,7 +632,7 @@ static int radius_server_request(struct radius_server_data *data, } if (sess->last_reply) { radius_msg_free(sess->last_reply); - free(sess->last_reply); + os_free(sess->last_reply); } sess->last_reply = reply; sess->last_from_port = from_port; @@ -633,7 +644,7 @@ static int radius_server_request(struct radius_server_data *data, client->counters.packets_dropped++; } - if (sess->eapSuccess || sess->eapFail) { + if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) { RADIUS_DEBUG("Removing completed session 0x%x after timeout", sess->sess_id); eloop_cancel_timeout(radius_server_session_remove_timeout, @@ -660,7 +671,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, char abuf[50]; int from_port = 0; - buf = malloc(RADIUS_MAX_MSG_LEN); + buf = os_malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { goto fail; } @@ -691,7 +702,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (!data->ipv6) { struct sockaddr_in *from4 = (struct sockaddr_in *) &from; - snprintf(abuf, sizeof(abuf), "%s", inet_ntoa(from4->sin_addr)); + os_strlcpy(abuf, inet_ntoa(from4->sin_addr), sizeof(abuf)); from_port = ntohs(from4->sin_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); @@ -715,7 +726,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, goto fail; } - free(buf); + os_free(buf); buf = NULL; if (wpa_debug_level <= MSG_MSGDUMP) { @@ -748,9 +759,9 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, fail: if (msg) { radius_msg_free(msg); - free(msg); + os_free(msg); } - free(buf); + os_free(buf); } @@ -765,7 +776,7 @@ static int radius_server_open_socket(int port) return -1; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { @@ -790,9 +801,9 @@ static int radius_server_open_socket6(int port) return -1; } - memset(&addr, 0, sizeof(addr)); + os_memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; - memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); addr.sin6_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind"); @@ -830,8 +841,8 @@ static void radius_server_free_clients(struct radius_server_data *data, client = client->next; radius_server_free_sessions(data, prev->sessions); - free(prev->shared_secret); - free(prev); + os_free(prev->shared_secret); + os_free(prev); } } @@ -856,7 +867,7 @@ radius_server_read_clients(const char *client_file, int ipv6) return NULL; } - buf = malloc(buf_size); + buf = os_malloc(buf_size); if (buf == NULL) { fclose(f); return NULL; @@ -919,10 +930,11 @@ radius_server_read_clients(const char *client_file, int ipv6) /* Convert IPv4 address to IPv6 */ if (mask <= 32) mask += (128 - 32); - memset(addr6.s6_addr, 0, 10); + os_memset(addr6.s6_addr, 0, 10); addr6.s6_addr[10] = 0xff; addr6.s6_addr[11] = 0xff; - memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 4); + os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, + 4); } #endif /* CONFIG_IPV6 */ @@ -935,18 +947,18 @@ radius_server_read_clients(const char *client_file, int ipv6) break; } - entry = wpa_zalloc(sizeof(*entry)); + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { failed = 1; break; } - entry->shared_secret = strdup(pos); + entry->shared_secret = os_strdup(pos); if (entry->shared_secret == NULL) { failed = 1; - free(entry); + os_free(entry); break; } - entry->shared_secret_len = strlen(entry->shared_secret); + entry->shared_secret_len = os_strlen(entry->shared_secret); entry->addr.s_addr = addr.s_addr; if (!ipv6) { val = 0; @@ -958,8 +970,8 @@ radius_server_read_clients(const char *client_file, int ipv6) if (ipv6) { int offset = mask / 8; - memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); - memset(entry->mask6.s6_addr, 0xff, offset); + os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); + os_memset(entry->mask6.s6_addr, 0xff, offset); val = 0; for (i = 0; i < (mask % 8); i++) val |= 1 << (7 - i); @@ -982,7 +994,7 @@ radius_server_read_clients(const char *client_file, int ipv6) clients = NULL; } - free(buf); + os_free(buf); fclose(f); return clients; @@ -1002,15 +1014,45 @@ radius_server_init(struct radius_server_conf *conf) } #endif /* CONFIG_IPV6 */ - data = wpa_zalloc(sizeof(*data)); + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; os_get_time(&data->start_time); - data->hostapd_conf = conf->hostapd_conf; + data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; data->ipv6 = conf->ipv6; + if (conf->pac_opaque_encr_key) { + data->pac_opaque_encr_key = os_malloc(16); + os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, + 16); + } + if (conf->eap_fast_a_id) { + data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); + if (data->eap_fast_a_id) { + os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id, + conf->eap_fast_a_id_len); + data->eap_fast_a_id_len = conf->eap_fast_a_id_len; + } + } + if (conf->eap_fast_a_id_info) + data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); + data->eap_fast_prov = conf->eap_fast_prov; + data->pac_key_lifetime = conf->pac_key_lifetime; + data->pac_key_refresh_time = conf->pac_key_refresh_time; + data->get_eap_user = conf->get_eap_user; + data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + data->tnc = conf->tnc; + data->wps = conf->wps; + if (conf->eap_req_id_text) { + data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); + if (data->eap_req_id_text) { + os_memcpy(data->eap_req_id_text, conf->eap_req_id_text, + conf->eap_req_id_text_len); + data->eap_req_id_text_len = conf->eap_req_id_text_len; + } + } data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); @@ -1055,7 +1097,11 @@ void radius_server_deinit(struct radius_server_data *data) radius_server_free_clients(data, data->clients); - free(data); + os_free(data->pac_opaque_encr_key); + os_free(data->eap_fast_a_id); + os_free(data->eap_fast_a_id_info); + os_free(data->eap_req_id_text); + os_free(data); } @@ -1079,40 +1125,40 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, os_get_time(&now); uptime = (now.sec - data->start_time.sec) * 100 + ((now.usec - data->start_time.usec) / 10000) % 100; - ret = snprintf(pos, end - pos, - "RADIUS-AUTH-SERVER-MIB\n" - "radiusAuthServIdent=hostapd\n" - "radiusAuthServUpTime=%d\n" - "radiusAuthServResetTime=0\n" - "radiusAuthServConfigReset=4\n", - uptime); + ret = os_snprintf(pos, end - pos, + "RADIUS-AUTH-SERVER-MIB\n" + "radiusAuthServIdent=hostapd\n" + "radiusAuthServUpTime=%d\n" + "radiusAuthServResetTime=0\n" + "radiusAuthServConfigReset=4\n", + uptime); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; } pos += ret; - ret = snprintf(pos, end - pos, - "radiusAuthServTotalAccessRequests=%u\n" - "radiusAuthServTotalInvalidRequests=%u\n" - "radiusAuthServTotalDupAccessRequests=%u\n" - "radiusAuthServTotalAccessAccepts=%u\n" - "radiusAuthServTotalAccessRejects=%u\n" - "radiusAuthServTotalAccessChallenges=%u\n" - "radiusAuthServTotalMalformedAccessRequests=%u\n" - "radiusAuthServTotalBadAuthenticators=%u\n" - "radiusAuthServTotalPacketsDropped=%u\n" - "radiusAuthServTotalUnknownTypes=%u\n", - data->counters.access_requests, - data->counters.invalid_requests, - data->counters.dup_access_requests, - data->counters.access_accepts, - data->counters.access_rejects, - data->counters.access_challenges, - data->counters.malformed_access_requests, - data->counters.bad_authenticators, - data->counters.packets_dropped, - data->counters.unknown_types); + ret = os_snprintf(pos, end - pos, + "radiusAuthServTotalAccessRequests=%u\n" + "radiusAuthServTotalInvalidRequests=%u\n" + "radiusAuthServTotalDupAccessRequests=%u\n" + "radiusAuthServTotalAccessAccepts=%u\n" + "radiusAuthServTotalAccessRejects=%u\n" + "radiusAuthServTotalAccessChallenges=%u\n" + "radiusAuthServTotalMalformedAccessRequests=%u\n" + "radiusAuthServTotalBadAuthenticators=%u\n" + "radiusAuthServTotalPacketsDropped=%u\n" + "radiusAuthServTotalUnknownTypes=%u\n", + data->counters.access_requests, + data->counters.invalid_requests, + data->counters.dup_access_requests, + data->counters.access_accepts, + data->counters.access_rejects, + data->counters.access_challenges, + data->counters.malformed_access_requests, + data->counters.bad_authenticators, + data->counters.packets_dropped, + data->counters.unknown_types); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; @@ -1132,35 +1178,33 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, } #endif /* CONFIG_IPV6 */ if (!data->ipv6) { - snprintf(abuf, sizeof(abuf), "%s", - inet_ntoa(cli->addr)); - snprintf(mbuf, sizeof(mbuf), "%s", - inet_ntoa(cli->mask)); + os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf)); + os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf)); } - ret = snprintf(pos, end - pos, - "radiusAuthClientIndex=%u\n" - "radiusAuthClientAddress=%s/%s\n" - "radiusAuthServAccessRequests=%u\n" - "radiusAuthServDupAccessRequests=%u\n" - "radiusAuthServAccessAccepts=%u\n" - "radiusAuthServAccessRejects=%u\n" - "radiusAuthServAccessChallenges=%u\n" - "radiusAuthServMalformedAccessRequests=%u\n" - "radiusAuthServBadAuthenticators=%u\n" - "radiusAuthServPacketsDropped=%u\n" - "radiusAuthServUnknownTypes=%u\n", - idx, - abuf, mbuf, - cli->counters.access_requests, - cli->counters.dup_access_requests, - cli->counters.access_accepts, - cli->counters.access_rejects, - cli->counters.access_challenges, - cli->counters.malformed_access_requests, - cli->counters.bad_authenticators, - cli->counters.packets_dropped, - cli->counters.unknown_types); + ret = os_snprintf(pos, end - pos, + "radiusAuthClientIndex=%u\n" + "radiusAuthClientAddress=%s/%s\n" + "radiusAuthServAccessRequests=%u\n" + "radiusAuthServDupAccessRequests=%u\n" + "radiusAuthServAccessAccepts=%u\n" + "radiusAuthServAccessRejects=%u\n" + "radiusAuthServAccessChallenges=%u\n" + "radiusAuthServMalformedAccessRequests=%u\n" + "radiusAuthServBadAuthenticators=%u\n" + "radiusAuthServPacketsDropped=%u\n" + "radiusAuthServUnknownTypes=%u\n", + idx, + abuf, mbuf, + cli->counters.access_requests, + cli->counters.dup_access_requests, + cli->counters.access_accepts, + cli->counters.access_rejects, + cli->counters.access_challenges, + cli->counters.malformed_access_requests, + cli->counters.bad_authenticators, + cli->counters.packets_dropped, + cli->counters.unknown_types); if (ret < 0 || ret >= end - pos) { *pos = '\0'; return pos - buf; @@ -1172,154 +1216,31 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, } -static Boolean radius_server_get_bool(void *ctx, enum eapol_bool_var variable) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return FALSE; - switch (variable) { - case EAPOL_eapSuccess: - return sess->eapSuccess; - case EAPOL_eapRestart: - return sess->eapRestart; - case EAPOL_eapFail: - return sess->eapFail; - case EAPOL_eapResp: - return sess->eapResp; - case EAPOL_eapReq: - return sess->eapReq; - case EAPOL_eapNoReq: - return sess->eapNoReq; - case EAPOL_portEnabled: - return sess->portEnabled; - case EAPOL_eapTimeout: - return sess->eapTimeout; - } - return FALSE; -} - - -static void radius_server_set_bool(void *ctx, enum eapol_bool_var variable, - Boolean value) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return; - switch (variable) { - case EAPOL_eapSuccess: - sess->eapSuccess = value; - break; - case EAPOL_eapRestart: - sess->eapRestart = value; - break; - case EAPOL_eapFail: - sess->eapFail = value; - break; - case EAPOL_eapResp: - sess->eapResp = value; - break; - case EAPOL_eapReq: - sess->eapReq = value; - break; - case EAPOL_eapNoReq: - sess->eapNoReq = value; - break; - case EAPOL_portEnabled: - sess->portEnabled = value; - break; - case EAPOL_eapTimeout: - sess->eapTimeout = value; - break; - } -} - - -static void radius_server_set_eapReqData(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return; - - free(sess->eapReqData); - sess->eapReqData = malloc(eapReqDataLen); - if (sess->eapReqData) { - memcpy(sess->eapReqData, eapReqData, eapReqDataLen); - sess->eapReqDataLen = eapReqDataLen; - } else { - sess->eapReqDataLen = 0; - } -} - - -static void radius_server_set_eapKeyData(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen) -{ - struct radius_session *sess = ctx; - - if (sess == NULL) - return; - - free(sess->eapKeyData); - if (eapKeyData) { - sess->eapKeyData = malloc(eapKeyDataLen); - if (sess->eapKeyData) { - memcpy(sess->eapKeyData, eapKeyData, eapKeyDataLen); - sess->eapKeyDataLen = eapKeyDataLen; - } else { - sess->eapKeyDataLen = 0; - } - } else { - sess->eapKeyData = NULL; - sess->eapKeyDataLen = 0; - } -} - - static int radius_server_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { struct radius_session *sess = ctx; - const struct hostapd_eap_user *eap_user; - int i, count; - - eap_user = hostapd_get_eap_user(sess->server->hostapd_conf, identity, - identity_len, phase2); - if (eap_user == NULL) - return -1; + struct radius_server_data *data = sess->server; - memset(user, 0, sizeof(*user)); - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { - user->methods[i].vendor = eap_user->methods[i].vendor; - user->methods[i].method = eap_user->methods[i].method; - } + return data->get_eap_user(data->conf_ctx, identity, identity_len, + phase2, user); +} - if (eap_user->password) { - user->password = malloc(eap_user->password_len); - if (user->password == NULL) - return -1; - memcpy(user->password, eap_user->password, - eap_user->password_len); - user->password_len = eap_user->password_len; - user->password_hash = eap_user->password_hash; - } - user->force_version = eap_user->force_version; - return 0; +static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + *len = data->eap_req_id_text_len; + return data->eap_req_id_text; } static struct eapol_callbacks radius_server_eapol_cb = { - .get_bool = radius_server_get_bool, - .set_bool = radius_server_set_bool, - .set_eapReqData = radius_server_set_eapReqData, - .set_eapKeyData = radius_server_set_eapKeyData, .get_eap_user = radius_server_get_eap_user, + .get_eap_req_id_text = radius_server_get_eap_req_id_text, }; @@ -1361,5 +1282,5 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) return; /* msg was stored with the session */ radius_msg_free(msg); - free(msg); + os_free(msg); } diff --git a/contrib/hostapd/radius_server.h b/src/radius/radius_server.h index 9c9315e60706f..d5fb6a1e3fee0 100644 --- a/contrib/hostapd/radius_server.h +++ b/src/radius/radius_server.h @@ -1,6 +1,6 @@ /* * hostapd / RADIUS authentication server - * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,14 +16,29 @@ #define RADIUS_SERVER_H struct radius_server_data; +struct eap_user; struct radius_server_conf { int auth_port; char *client_file; - void *hostapd_conf; + void *conf_ctx; void *eap_sim_db_priv; void *ssl_ctx; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; int ipv6; + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + const char *eap_req_id_text; + size_t eap_req_id_text_len; }; diff --git a/src/rsn_supp/.gitignore b/src/rsn_supp/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/rsn_supp/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/rsn_supp/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c new file mode 100644 index 0000000000000..45c256a0277f8 --- /dev/null +++ b/src/rsn_supp/peerkey.c @@ -0,0 +1,1182 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_PEERKEY + +#include "common.h" +#include "sha1.h" +#include "sha256.h" +#include "eloop.h" +#include "wpa.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "ieee802_11_defs.h" +#include "peerkey.h" + + +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) +{ + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + return pos; +} + + +static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_sm *sm = eloop_ctx; + struct wpa_peerkey *peerkey = timeout_ctx; +#endif + /* TODO: time out SMK and any STK that was generated using this SMK */ +} + + +static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); + os_free(peerkey); +} + + +static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, + const u8 *peer, + u16 mui, u16 error_type, int ver) +{ + size_t rlen; + struct wpa_eapol_key *err; + struct rsn_error_kde error; + u8 *rbuf, *pos; + size_t kde_len; + u16 key_info; + + kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); + if (peer) + kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*err) + kde_len, &rlen, + (void *) &err); + if (rbuf == NULL) + return -1; + + err->type = EAPOL_KEY_TYPE_RSN; + key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | + WPA_KEY_INFO_REQUEST; + WPA_PUT_BE16(err->key_info, key_info); + WPA_PUT_BE16(err->key_length, 0); + os_memcpy(err->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(err->key_data_length, (u16) kde_len); + pos = (u8 *) (err + 1); + + if (peer) { + /* Peer MAC Address KDE */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); + } + + /* Error KDE */ + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); + + if (peer) { + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " + MACSTR " mui %d error_type %d)", + MAC2STR(peer), mui, error_type); + } else { + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " + "(mui %d error_type %d)", mui, error_type); + } + + wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, err->key_mic); + + return 0; +} + + +static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, struct wpa_peerkey *peerkey) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf, *pos; + size_t kde_len; + u16 key_info; + + /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ + kde_len = peerkey->rsnie_p_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*reply) + kde_len, &rlen, + (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = EAPOL_KEY_TYPE_RSN; + key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); + + WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); + pos = (u8 *) (reply + 1); + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); + + /* Initiator MAC Address KDE */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); + + /* Initiator Nonce */ + wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static int wpa_supplicant_process_smk_m2( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct rsn_ie_hdr *hdr; + u8 *pos; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); + return -1; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M2"); + return -1; + } + + wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, + MAC2STR(kde.mac_addr)); + + if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " + "M2"); + return -1; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); + return -1; + } + + cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; + if (cipher & WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); + cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); + cipher = WPA_CIPHER_TKIP; + } else { + wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); + wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, + STK_MUI_SMK, STK_ERR_CPHR_NS, + ver); + return -1; + } + + /* TODO: find existing entry and if found, use that instead of adding + * a new one; how to handle the case where both ends initiate at the + * same time? */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); + os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); + os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peerkey->rsnie_i_len = kde.rsn_ie_len; + peerkey->cipher = cipher; +#ifdef CONFIG_IEEE80211W + if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256)) + peerkey->use_sha256 = 1; +#endif /* CONFIG_IEEE80211W */ + + if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Failed to get random data for PNonce"); + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + /* Include only the selected cipher in pairwise cipher suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + if (cipher == WPA_CIPHER_CCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (cipher == WPA_CIPHER_TKIP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + + hdr->len = (pos - peerkey->rsnie_p) - 2; + peerkey->rsnie_p_len = pos - peerkey->rsnie_p; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_p, peerkey->rsnie_p_len); + + wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); + + peerkey->next = sm->peerkey; + sm->peerkey = peerkey; + + return 0; +} + + +/** + * rsn_smkid - Derive SMK identifier + * @smk: Station master key (32 bytes) + * @pnonce: Peer Nonce + * @mac_p: Peer MAC address + * @inonce: Initiator Nonce + * @mac_i: Initiator MAC address + * @use_sha256: Whether to use SHA256-based KDF + * + * 8.5.1.4 Station to station (STK) key hierarchy + * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) + */ +static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, + const u8 *inonce, const u8 *mac_i, u8 *smkid, + int use_sha256) +{ + char *title = "SMK Name"; + const u8 *addr[5]; + const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, + ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = pnonce; + addr[2] = mac_p; + addr[3] = inonce; + addr[4] = mac_i; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); + os_memcpy(smkid, hash, PMKID_LEN); +} + + +static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf; + size_t kde_len; + u16 key_info, ver; + + kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher == WPA_CIPHER_CCMP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, + peerkey->smkid, PMKID_LEN); + + if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "RSN: Failed to get random data for INonce (STK)"); + os_free(mbuf); + return; + } + wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", + peerkey->inonce, WPA_NONCE_LEN); + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, + mbuf, mlen, NULL); +} + + +static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf, *pos; + size_t kde_len; + u16 key_info, ver; + be32 lifetime; + + kde_len = peerkey->rsnie_i_len + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | + WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher == WPA_CIPHER_CCMP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + pos = (u8 *) (msg + 1); + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + lifetime = host_to_be32(peerkey->lifetime); + wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime)); + + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, + ETH_P_EAPOL, mbuf, mlen, msg->key_mic); +} + + +static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", + MAC2STR(kde->mac_addr)); + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " + "match with the one used in SMK M3"); + return -1; + } + + if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " + "match with the one received in SMK M2"); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, + struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + int cipher; + struct wpa_ie_data ie; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", + MAC2STR(kde->mac_addr)); + if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || + wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); + /* TODO: abort negotiation */ + return -1; + } + + if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " + "not match with INonce used in SMK M1"); + return -1; + } + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " + "match with the one used in SMK M1"); + return -1; + } + + os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); + peerkey->rsnie_p_len = kde->rsn_ie_len; + os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); + + cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; + if (cipher & WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); + peerkey->cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); + peerkey->cipher = WPA_CIPHER_TKIP; + } else { + wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " + "unacceptable cipher", MAC2STR(kde->mac_addr)); + wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, + STK_MUI_SMK, STK_ERR_CPHR_NS, + ver); + /* TODO: abort negotiation */ + return -1; + } + + return 0; +} + + +static int wpa_supplicant_process_smk_m45( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + u32 lifetime; + struct os_time now; + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); + return -1; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || + kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || + kde.lifetime == NULL || kde.lifetime_len < 4) { + wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " + "Lifetime KDE in SMK M4/M5"); + return -1; + } + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && + os_memcmp(peerkey->initiator ? peerkey->inonce : + peerkey->pnonce, + key->key_nonce, WPA_NONCE_LEN) == 0) + break; + } + if (peerkey == NULL) { + wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " + "for SMK M4/M5: peer " MACSTR, + MAC2STR(kde.mac_addr)); + return -1; + } + + if (peerkey->initiator) { + if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, + peerkey, &kde) < 0) + return -1; + } else { + if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) + return -1; + } + + os_memcpy(peerkey->smk, kde.smk, PMK_LEN); + peerkey->smk_complete = 1; + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); + lifetime = WPA_GET_BE32(kde.lifetime); + wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); + if (lifetime > 1000000000) + lifetime = 1000000000; /* avoid overflowing expiration time */ + peerkey->lifetime = lifetime; + os_get_time(&now); + peerkey->expiration = now.sec + lifetime; + eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, + sm, peerkey); + + if (peerkey->initiator) { + rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, + peerkey->inonce, sm->own_addr, peerkey->smkid, + peerkey->use_sha256); + wpa_supplicant_send_stk_1_of_4(sm, peerkey); + } else { + rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, + peerkey->inonce, peerkey->addr, peerkey->smkid, + peerkey->use_sha256); + } + wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); + + return 0; +} + + +static int wpa_supplicant_process_smk_error( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len) +{ + struct wpa_eapol_ie_parse kde; + struct rsn_error_kde error; + u8 peer[ETH_ALEN]; + u16 error_type; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return -1; + } + + if (kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); + return -1; + } + + if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) + os_memcpy(peer, kde.mac_addr, ETH_ALEN); + os_memcpy(&error, kde.error, sizeof(error)); + error_type = be_to_host16(error.error_type); + wpa_msg(sm->ctx->ctx, MSG_INFO, + "RSN: SMK Error KDE received: MUI %d error_type %d peer " + MACSTR, + be_to_host16(error.mui), error_type, + MAC2STR(peer)); + + if (kde.mac_addr && + (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || + error_type == STK_ERR_CPHR_NS)) { + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == + 0) + break; + } + if (peerkey == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " + "found for SMK Error"); + return -1; + } + /* TODO: abort SMK/STK handshake and remove all related keys */ + } + + return 0; +} + + +static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + const u8 *kde; + size_t len, kde_buf_len; + struct wpa_ptk *stk; + u8 buf[8], *kde_buf, *pos; + be32 lifetime; + + wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&ie, 0, sizeof(ie)); + + /* RSN: msg 1/4 should contain SMKID for the selected SMK */ + kde = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); + if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); + return; + } + if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", + ie.pmkid, PMKID_LEN); + return; + } + + if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "RSN: Failed to get random data for PNonce"); + return; + } + wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", + peerkey->pnonce, WPA_NONCE_LEN); + + /* Calculate STK which will be stored as a temporary STK until it has + * been verified when processing message 3/4. */ + stk = &peerkey->tstk; + wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", + sm->own_addr, peerkey->addr, + peerkey->pnonce, key->key_nonce, + (u8 *) stk, sizeof(*stk), + peerkey->use_sha256); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, stk->u.auth.tx_mic_key, 8); + os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); + os_memcpy(stk->u.auth.rx_mic_key, buf, 8); + peerkey->tstk_set = 1; + + kde_buf_len = peerkey->rsnie_p_len + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + + 2 + RSN_SELECTOR_LEN + PMKID_LEN; + kde_buf = os_malloc(kde_buf_len); + if (kde_buf == NULL) + return; + pos = kde_buf; + pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); + lifetime = host_to_be32(peerkey->lifetime); + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime)); + wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); + + if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, + peerkey->pnonce, kde_buf, kde_buf_len, + stk)) { + os_free(kde_buf); + return; + } + os_free(kde_buf); + + os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); +} + + +static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + u32 lifetime; + struct os_time now; + + if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) + return; + + lifetime = WPA_GET_BE32(kde->lifetime); + + if (lifetime >= peerkey->lifetime) { + wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " + "which is larger than or equal to own value %u " + "seconds - ignored", lifetime, peerkey->lifetime); + return; + } + + wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " + "(own was %u seconds) - updated", + lifetime, peerkey->lifetime); + peerkey->lifetime = lifetime; + + os_get_time(&now); + peerkey->expiration = now.sec + lifetime; + eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); + eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, + sm, peerkey); +} + + +static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse kde; + const u8 *keydata; + size_t len; + + wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&kde, 0, sizeof(kde)); + + /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE + * from the peer. It may also include Lifetime KDE. */ + keydata = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); + if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || + kde.pmkid == NULL || kde.rsn_ie == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); + return; + } + + if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", + kde.pmkid, PMKID_LEN); + return; + } + + if (kde.rsn_ie_len != peerkey->rsnie_p_len || + os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { + wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " + "handshakes did not match"); + wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", + peerkey->rsnie_p, peerkey->rsnie_p_len); + wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", + kde.rsn_ie, kde.rsn_ie_len); + return; + } + + wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); + + wpa_supplicant_send_stk_3_of_4(sm, peerkey); + os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); +} + + +static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse kde; + const u8 *keydata; + size_t len, key_len; + const u8 *_key; + u8 key_buf[32], rsc[6]; + + wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&kde, 0, sizeof(kde)); + + /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include + * Lifetime KDE. */ + keydata = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); + if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { + wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " + "STK 3/4"); + return; + } + + if (kde.rsn_ie_len != peerkey->rsnie_i_len || + os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { + wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " + "handshakes did not match"); + wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " + "handshake", + peerkey->rsnie_i, peerkey->rsnie_i_len); + wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " + "handshake", + kde.rsn_ie, kde.rsn_ie_len); + return; + } + + if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " + "4-Way Handshake differs from 3 of STK 4-Way " + "Handshake - drop packet (src=" MACSTR ")", + MAC2STR(peerkey->addr)); + return; + } + + wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); + + if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, + WPA_GET_BE16(key->key_info), + NULL, 0, &peerkey->stk)) + return; + + _key = (u8 *) peerkey->stk.tk1; + if (peerkey->cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + os_memcpy(key_buf, _key, 16); + os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8); + os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); + _key = key_buf; + key_len = 32; + } else + key_len = 16; + + os_memset(rsc, 0, 6); + if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, + rsc, sizeof(rsc), _key, key_len) < 0) { + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } +} + + +static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + u8 rsc[6]; + + wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(rsc, 0, 6); + if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, + rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, + peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } +} + + +/** + * peerkey_verify_eapol_key_mic - Verify PeerKey MIC + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peerkey: Pointer to the PeerKey data for the peer + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @buf: Pointer to the beginning of EAPOL-Key frame + * @len: Length of the EAPOL-Key frame + * Returns: 0 on success, -1 on failure + */ +int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + if (peerkey->initiator && !peerkey->stk_set) { + wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", + sm->own_addr, peerkey->addr, + peerkey->inonce, key->key_nonce, + (u8 *) &peerkey->stk, sizeof(peerkey->stk), + peerkey->use_sha256); + peerkey->stk_set = 1; + } + + os_memcpy(mic, key->key_mic, 16); + if (peerkey->tstk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " + "when using TSTK - ignoring TSTK"); + } else { + ok = 1; + peerkey->tstk_set = 0; + peerkey->stk_set = 1; + os_memcpy(&peerkey->stk, &peerkey->tstk, + sizeof(peerkey->stk)); + } + } + + if (!ok && peerkey->stk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + + os_memcpy(peerkey->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + peerkey->replay_counter_set = 1; + return 0; +} + + +/** + * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peer: MAC address of the peer STA + * Returns: 0 on success, or -1 on failure + * + * Send an EAPOL-Key Request to the current authenticator to start STK + * handshake with the peer. + */ +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + size_t rlen, kde_len; + struct wpa_eapol_key *req; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + struct wpa_peerkey *peerkey; + struct wpa_ie_data ie; + + if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) + return -1; + + if (sm->ap_rsn_ie && + wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && + !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { + wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); + return -1; + } + + if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_sm_get_bssid(sm, bssid) < 0) { + wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " + "SMK M1"); + return -1; + } + + /* TODO: find existing entry and if found, use that instead of adding + * a new one */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + peerkey->initiator = 1; + os_memcpy(peerkey->addr, peer, ETH_ALEN); +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) + peerkey->use_sha256 = 1; +#endif /* CONFIG_IEEE80211W */ + + /* SMK M1: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) + */ + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = 0; + if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + } + if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + count++; + } + WPA_PUT_LE16(count_pos, count); + + hdr->len = (pos - peerkey->rsnie_i) - 2; + peerkey->rsnie_i_len = pos - peerkey->rsnie_i; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_i, peerkey->rsnie_i_len); + + kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*req) + kde_len, &rlen, + (void *) &req); + if (rbuf == NULL) { + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + + req->type = EAPOL_KEY_TYPE_RSN; + key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; + WPA_PUT_BE16(req->key_info, key_info); + WPA_PUT_BE16(req->key_length, 0); + os_memcpy(req->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Failed to get random data for INonce"); + os_free(rbuf); + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", + req->key_nonce, WPA_NONCE_LEN); + + WPA_PUT_BE16(req->key_data_length, (u16) kde_len); + pos = (u8 *) (req + 1); + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + /* Peer MAC address KDE */ + wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); + + wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " + MACSTR ")", MAC2STR(peer)); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, + rbuf, rlen, req->key_mic); + + peerkey->next = sm->peerkey; + sm->peerkey = peerkey; + + return 0; +} + + +/** + * peerkey_deinit - Free PeerKey values + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void peerkey_deinit(struct wpa_sm *sm) +{ + struct wpa_peerkey *prev, *peerkey = sm->peerkey; + while (peerkey) { + prev = peerkey; + peerkey = peerkey->next; + os_free(prev); + } +} + + +void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver) +{ + if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == + (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { + /* 3/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver); + } else if (key_info & WPA_KEY_INFO_ACK) { + /* 1/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver); + } else if (key_info & WPA_KEY_INFO_SECURE) { + /* 4/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); + } else { + /* 2/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver); + } +} + + +void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver) +{ + if (key_info & WPA_KEY_INFO_ERROR) { + /* SMK Error */ + wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); + } else if (key_info & WPA_KEY_INFO_ACK) { + /* SMK M2 */ + wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, + ver); + } else { + /* SMK M4 or M5 */ + wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, + ver); + } +} + +#endif /* CONFIG_PEERKEY */ diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h new file mode 100644 index 0000000000000..2613127c3ea48 --- /dev/null +++ b/src/rsn_supp/peerkey.h @@ -0,0 +1,87 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PEERKEY_H +#define PEERKEY_H + +#define PEERKEY_MAX_IE_LEN 80 +struct wpa_peerkey { + struct wpa_peerkey *next; + int initiator; /* whether this end was initator for SMK handshake */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ + u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u8 smk[PMK_LEN]; + int smk_complete; + u8 smkid[PMKID_LEN]; + u32 lifetime; + os_time_t expiration; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + int replay_counter_set; + int use_sha256; /* whether AKMP indicate SHA256-based derivations */ + + struct wpa_ptk stk, tstk; + int stk_set, tstk_set; +}; + + +#ifdef CONFIG_PEERKEY + +int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len); +void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver); +void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver); +void peerkey_deinit(struct wpa_sm *sm); + +#else /* CONFIG_PEERKEY */ + +static inline int +peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline void +peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver) +{ +} + +static inline void +peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver) +{ +} + +static inline void peerkey_deinit(struct wpa_sm *sm) +{ +} + +#endif /* CONFIG_PEERKEY */ + +#endif /* PEERKEY_H */ diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c new file mode 100644 index 0000000000000..f8373de6dd3d3 --- /dev/null +++ b/src/rsn_supp/pmksa_cache.c @@ -0,0 +1,511 @@ +/* + * WPA Supplicant - RSN PMKSA cache + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "eloop.h" +#include "sha1.h" +#include "sha256.h" +#include "wpa_i.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "pmksa_cache.h" + +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) + +static const int pmksa_cache_max_entries = 32; + +struct rsn_pmksa_cache { + struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, + int replace); + void *ctx; +}; + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, + const u8 *spa, u8 *pmkid, int use_sha256) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + os_memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + os_free(entry); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry, + int replace) +{ + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx, replace); + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_time now; + + os_get_time(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(pmksa, entry, 0); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + pmksa->sm->cur_pmksa = NULL; + eapol_sm_request_reauth(pmksa->sm->eapol); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_time(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); + + entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL); + if (entry) { + sec = pmksa->pmksa->reauth_time - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, + NULL); + } +} + + +/** + * pmksa_cache_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @network_ctx: Network configuration context for this PMK + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Authenticator, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK and the driver interface is notified of the new PMKID. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct os_time now; + + if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_time(&now); + entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; + entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * + pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; + entry->akmp = akmp; + os_memcpy(entry->aa, aa, ETH_ALEN); + entry->network_ctx = network_ctx; + + /* Replace an old entry for the same Authenticator (if found) with the + * new entry */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == pmk_len && + os_memcmp(pos->pmk, pmk, pmk_len) == 0 && + os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == + 0) { + wpa_printf(MSG_DEBUG, "WPA: reusing previous " + "PMKSA entry"); + os_free(entry); + return pos; + } + if (prev == NULL) + pmksa->pmksa = pos->next; + else + prev->next = pos->next; + if (pos == pmksa->sm->cur_pmksa) { + /* We are about to replace the current PMKSA + * cache entry. This happens when the PMKSA + * caching attempt fails, so we don't want to + * force pmksa_cache_free_entry() to disconnect + * at this point. Let's just make sure the old + * PMKSA cache entry will not be used in the + * future. + */ + wpa_printf(MSG_DEBUG, "RSN: replacing current " + "PMKSA entry"); + pmksa->sm->cur_pmksa = NULL; + } + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP"); + pmksa_cache_free_entry(pmksa, pos, 1); + break; + } + prev = pos; + pos = pos->next; + } + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = pmksa->pmksa; + pmksa->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(pmksa->sm, pos->aa, pos->pmkid); + pmksa_cache_free_entry(pmksa, pos, 0); + } + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + pmksa_cache_set_expiration(pmksa); + } else { + entry->next = prev->next; + prev->next = entry; + } + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->aa)); + wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); + + return entry; +} + + +/** + * pmksa_cache_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + */ +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + pmksa->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + os_free(prev); + } + pmksa_cache_set_expiration(pmksa); + os_free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + while (entry) { + if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * + * Clear references to old data structures when wpa_supplicant is reconfigured. + */ +void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + while (entry) { + entry->network_ctx = NULL; + entry = entry->next; + } +} + + +static struct rsn_pmksa_cache_entry * +pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *new_entry; + + new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, + aa, pmksa->sm->own_addr, + old_entry->network_ctx, old_entry->akmp); + if (new_entry == NULL) + return NULL; + + /* TODO: reorder entries based on expiration time? */ + new_entry->expiration = old_entry->expiration; + new_entry->opportunistic = 1; + + return new_entry; +} + + +/** + * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context + * @aa: Authenticator address for the new AP + * Returns: Pointer to a new PMKSA cache entry or %NULL if not available + * + * Try to create a new PMKSA cache entry opportunistically by guessing that the + * new AP is sharing the same PMK as another AP that has the same SSID and has + * already an entry in PMKSA cache. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + + if (network_ctx == NULL) + return NULL; + while (entry) { + if (entry->network_ctx == network_ctx) { + entry = pmksa_cache_clone_entry(pmksa, entry, aa); + if (entry) { + wpa_printf(MSG_DEBUG, "RSN: added " + "opportunistic PMKSA cache entry " + "for " MACSTR, MAC2STR(aa)); + } + return entry; + } + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_current - Get the current used PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to the current PMKSA cache entry or %NULL if not available + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return NULL; + return sm->cur_pmksa; +} + + +/** + * pmksa_cache_clear_current - Clear the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_clear_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_set_current - Set the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmkid: PMKID for selecting PMKSA or %NULL if not used + * @bssid: BSSID for PMKSA or %NULL if not used + * @network_ctx: Network configuration context + * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * Returns: 0 if PMKSA was found or -1 if no matching entry was found + */ +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic) +{ + struct rsn_pmksa_cache *pmksa = sm->pmksa; + sm->cur_pmksa = NULL; + if (pmkid) + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid); + if (sm->cur_pmksa == NULL && bssid) + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL); + if (sm->cur_pmksa == NULL && try_opportunistic && bssid) + sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, + network_ctx, + bssid); + if (sm->cur_pmksa) { + wpa_hexdump(MSG_DEBUG, "RSN: 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, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + + os_get_time(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + i = 0; + entry = sm->pmksa->pmksa; + while (entry) { + i++; + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} + + +/** + * pmksa_cache_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, int replace), + void *ctx, struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + pmksa->sm = sm; + } + + return pmksa; +} + +#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h new file mode 100644 index 0000000000000..a329b25b08fd9 --- /dev/null +++ b/src/rsn_supp/pmksa_cache.h @@ -0,0 +1,126 @@ +/* + * wpa_supplicant - WPA2/RSN PMKSA cache functions + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PMKSA_CACHE_H +#define PMKSA_CACHE_H + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; + + os_time_t reauth_time; + + /** + * network_ctx - Network configuration context + * + * This field is only used to match PMKSA cache entries to a specific + * network configuration (e.g., a specific SSID and security policy). + * This can be a pointer to the configuration entry, but PMKSA caching + * code does not dereference the value and this could be any kind of + * identifier. + */ + void *network_ctx; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, int replace), + void *ctx, struct wpa_sm *sm); +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid); +int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp); +void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); +void pmksa_cache_clear_current(struct wpa_sm *sm); +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic); +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, + void *network_ctx, const u8 *aa); + +#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ + +static inline struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, int replace), + void *ctx, struct wpa_sm *sm) +{ + return (void *) -1; +} + +static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get_current(struct wpa_sm *sm) +{ + return NULL; +} + +static inline int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ + return -1; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + return NULL; +} + +static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) +{ +} + +static inline void pmksa_cache_clear_current(struct wpa_sm *sm) +{ +} + +static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, + void *network_ctx, + int try_opportunistic) +{ + return -1; +} + +#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ + +#endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c new file mode 100644 index 0000000000000..b00c004cf62c8 --- /dev/null +++ b/src/rsn_supp/preauth.c @@ -0,0 +1,529 @@ +/* + * WPA Supplicant - RSN pre-authentication + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "drivers/driver.h" +#include "eloop.h" +#include "l2_packet/l2_packet.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" +#include "ieee802_11_defs.h" + + +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 + + +struct rsn_pmksa_candidate { + struct rsn_pmksa_candidate *next; + u8 bssid[ETH_ALEN]; + int priority; +}; + + +/** + * pmksa_candidate_free - Free all entries in PMKSA candidate list + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_candidate_free(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *entry, *prev; + + if (sm == NULL) + return; + + entry = sm->pmksa_candidates; + sm->pmksa_candidates = NULL; + while (entry) { + prev = entry; + entry = entry->next; + os_free(prev); + } +} + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (sm->preauth_eapol == NULL || + is_zero_ether_addr(sm->preauth_bssid) || + os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len); +} + + +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, 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); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", + pmk, pmk_len); + sm->pmk_len = pmk_len; + pmksa_cache_add(sm->pmksa, pmk, pmk_len, + sm->preauth_bssid, sm->own_addr, + sm->network_ctx, + WPA_KEY_MGMT_IEEE8021X); + } else { + wpa_msg(sm->ctx->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); + os_free(msg); + return res; +} + + +/** + * rsn_preauth_init - Start new RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Authenticator address (BSSID) with which to preauthenticate + * @eap_conf: Current EAP configuration + * Returns: 0 on success, -1 on another pre-authentication is in progress, + * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine + * initialization failure, -4 on memory allocation failure + * + * This function request an RSN pre-authentication with a given destination + * address. This is usually called for PMKSA candidates found from scan results + * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger + * pre-authentication. + */ +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + if (sm->preauth_eapol) + return -1; + + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: starting pre-authentication " + "with " MACSTR, MAC2STR(dst)); + + sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, sm, 0); + if (sm->l2_preauth == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " + "processing for pre-authentication"); + return -2; + } + + if (sm->bridge_ifname) { + sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname, + sm->own_addr, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, sm, 0); + if (sm->l2_preauth_br == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " + "packet processing (bridge) for " + "pre-authentication"); + return -2; + } + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + return -4; + } + ctx->ctx = sm->ctx->ctx; + ctx->msg_ctx = sm->ctx->ctx; + ctx->preauth = 1; + ctx->cb = rsn_preauth_eapol_cb; + ctx->cb_ctx = sm; + ctx->scard_ctx = sm->scard_ctx; + ctx->eapol_send = rsn_preauth_eapol_send; + ctx->eapol_send_ctx = sm; + ctx->set_config_blob = sm->ctx->set_config_blob; + ctx->get_config_blob = sm->ctx->get_config_blob; + + sm->preauth_eapol = eapol_sm_init(ctx); + if (sm->preauth_eapol == NULL) { + os_free(ctx); + wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " + "state machines for pre-authentication"); + return -3; + } + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 0; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = sm->fast_reauth; + eapol_conf.workaround = sm->eap_workaround; + eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf); + /* + * Use a shorter startPeriod with preauthentication since the first + * preauth EAPOL-Start frame may end up being dropped due to race + * condition in the AP between the data receive and key configuration + * after the 4-Way Handshake. + */ + eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6); + os_memcpy(sm->preauth_bssid, dst, ETH_ALEN); + + eapol_sm_notify_portValid(sm->preauth_eapol, TRUE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE); + + eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0, + rsn_preauth_timeout, sm, NULL); + + return 0; +} + + +/** + * rsn_preauth_deinit - Abort RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * This function aborts the current RSN pre-authentication (if one is started) + * and frees resources allocated for it. + */ +void rsn_preauth_deinit(struct wpa_sm *sm) +{ + if (sm == NULL || !sm->preauth_eapol) + return; + + eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL); + eapol_sm_deinit(sm->preauth_eapol); + sm->preauth_eapol = NULL; + os_memset(sm->preauth_bssid, 0, ETH_ALEN); + + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; + if (sm->l2_preauth_br) { + l2_packet_deinit(sm->l2_preauth_br); + sm->l2_preauth_br = NULL; + } +} + + +/** + * rsn_preauth_candidate_process - Process PMKSA candidates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Go through the PMKSA candidates and start pre-authentication if a candidate + * without an existing PMKSA cache entry is found. Processed candidates will be + * removed from the list. + */ +void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *candidate; + + 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 && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) { + 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_entry *p = NULL; + candidate = sm->pmksa_candidates; + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL); + if (os_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->eap_conf_ctx); + os_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; + os_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->network_ctx && sm->proactive_key_caching) + pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, + bssid); + + if (!preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + /* If BSSID already on candidate list, update the priority of the old + * entry. Do not override priority based on normal scan results. */ + prev = NULL; + cand = sm->pmksa_candidates; + while (cand) { + if (os_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 = os_zalloc(sizeof(*cand)); + if (cand == NULL) + return; + os_memcpy(cand->bssid, bssid, ETH_ALEN); + cand->priority = prio; + } + + /* Add candidate to the list; order by increasing priority value. i.e., + * highest priority (smallest value) first. */ + 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 + * + * 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_results *results) +{ + struct wpa_scan_res *r; + struct wpa_ie_data ie; + int i; + struct rsn_pmksa_cache_entry *pmksa; + + if (sm->ssid_len == 0) + return; + + /* + * TODO: is it ok to free all candidates? What about the entries + * received from EVENT_PMKID_CANDIDATE? + */ + pmksa_candidate_free(sm); + + for (i = results->num - 1; i >= 0; i--) { + const u8 *ssid, *rsn; + + r = results->res[i]; + + ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); + if (ssid == NULL || ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, ssid[1]) != 0) + continue; + + if (os_memcmp(r->bssid, sm->bssid, ETH_ALEN) == 0) + continue; + + rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); + if (rsn == NULL || wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + continue; + + pmksa = pmksa_cache_get(sm->pmksa, 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); + } +} + + +#ifdef CONFIG_CTRL_IFACE +/** + * rsn_preauth_get_status - Get pre-authentication status + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA2 pre-authentication for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int res, ret; + + if (sm->preauth_eapol) { + ret = os_snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + res = eapol_sm_get_status(sm->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} +#endif /* CONFIG_CTRL_IFACE */ + + +/** + * rsn_preauth_in_progress - Verify whether pre-authentication is in progress + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return sm->preauth_eapol != NULL; +} + +#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h new file mode 100644 index 0000000000000..b9ac57b531ba4 --- /dev/null +++ b/src/rsn_supp/preauth.h @@ -0,0 +1,78 @@ +/* + * wpa_supplicant - WPA2/RSN pre-authentication functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +struct wpa_scan_results; + +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) + +void pmksa_candidate_free(struct wpa_sm *sm); +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf); +void rsn_preauth_deinit(struct wpa_sm *sm); +void rsn_preauth_scan_results(struct wpa_sm *sm, + struct wpa_scan_results *results); +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_WPA2 */ + +static inline void pmksa_candidate_free(struct wpa_sm *sm) +{ +} + +static inline void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf) +{ + return -1; +} + +static inline void rsn_preauth_deinit(struct wpa_sm *sm) +{ +} +static inline void rsn_preauth_scan_results(struct wpa_sm *sm, + struct wpa_scan_results *results) +{ +} + +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_WPA2 */ + +#endif /* PREAUTH_H */ diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c new file mode 100644 index 0000000000000..b22147645a12d --- /dev/null +++ b/src/rsn_supp/wpa.c @@ -0,0 +1,2387 @@ +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "wpa.h" +#include "eloop.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "peerkey.h" +#include "ieee802_11_defs.h" + + +/** + * 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"; + } +} + + +/** + * 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)"; +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return "FT-EAP"; + case WPA_KEY_MGMT_FT_PSK: + return "FT-PSK"; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return "WPA2-EAP-SHA256"; + case WPA_KEY_MGMT_PSK_SHA256: + return "WPA2-PSK-SHA256"; +#endif /* CONFIG_IEEE80211W */ + default: + return "UNKNOWN"; + } +} + + +/** + * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @kck: Key Confirmation Key (KCK, part of PTK) + * @ver: Version field from Key Info + * @dest: Destination address for the frame + * @proto: Ethertype (usually ETH_P_EAPOL) + * @msg: EAPOL-Key message + * @msg_len: Length of message + * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + */ +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) +{ + if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { + /* + * Association event was not yet received; try to fetch + * BSSID from the driver. + */ + if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { + wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); + } else { + dest = sm->bssid; + wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR + ") as the destination for EAPOL-Key", + MAC2STR(dest)); + } + } + if (key_mic) + wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic); + wpa_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); + os_free(msg); +} + + +/** + * wpa_sm_key_request - Send EAPOL-Key Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @error: Indicate whether this is an Michael MIC error report + * @pairwise: 1 = error report for pairwise packet, 0 = for group packet + * + * Send an EAPOL-Key Request to the current authenticator. This function is + * used to request rekeying and it is usually called when a local Michael MIC + * failure is detected. + */ +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) +{ + size_t rlen; + struct wpa_eapol_key *reply; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf; + + if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_sm_get_bssid(sm, bssid) < 0) { + wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " + "request"); + return; + } + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info = WPA_KEY_INFO_REQUEST | ver; + if (sm->ptk_set) + key_info |= WPA_KEY_INFO_MIC; + if (error) + key_info |= WPA_KEY_INFO_ERROR; + if (pairwise) + key_info |= WPA_KEY_INFO_KEY_TYPE; + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + os_memcpy(reply->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " + "pairwise=%d ptk_set=%d len=%lu)", + error, pairwise, sm->ptk_set, (unsigned long) rlen); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, + rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? + reply->key_mic : NULL); +} + + +static int wpa_supplicant_get_pmk(struct wpa_sm *sm, + const unsigned char *src_addr, + const u8 *pmkid) +{ + int abort_cached = 0; + + if (pmkid && !sm->cur_pmksa) { + /* When using drivers that generate RSN IE, wpa_supplicant may + * not have enough time to get the association information + * event before receiving this 1/4 message, so try to find a + * matching PMKSA cache entry here. */ + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid); + if (sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " + "PMKSA cache"); + } else { + wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && sm->cur_pmksa && + os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); + wpa_sm_set_pmk_from_pmksa(sm); + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", + sm->pmk, sm->pmk_len); + eapol_sm_notify_cached(sm->eapol); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); + pmk_len = 16; + } else { +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; + if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) + { + os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + os_memset(buf, 0, sizeof(buf)); + } +#endif /* CONFIG_IEEE80211R */ + } + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " + "machines", sm->pmk, pmk_len); + sm->pmk_len = pmk_len; + pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr, + sm->own_addr, sm->network_ctx, + sm->key_mgmt); + if (!sm->cur_pmksa && pmkid && + pmksa_cache_get(sm->pmksa, src_addr, pmkid)) { + wpa_printf(MSG_DEBUG, "RSN: the new PMK " + "matches with the PMKID"); + abort_cached = 0; + } + } else { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines"); + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Key handshake aborted"); + if (sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " + "caching attempt"); + sm->cur_pmksa = NULL; + abort_cached = 1; + } else if (!abort_cached) { + return -1; + } + } + } + + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) { + /* 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 authentication"); + buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, + NULL, 0, &buflen, NULL); + if (buf) { + wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, + buf, buflen); + os_free(buf); + } + + return -1; + } + + return 0; +} + + +/** + * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @nonce: Nonce value for the EAPOL-Key frame + * @wpa_ie: WPA/RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + int ver, const u8 *nonce, + const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + if (wpa_ie == NULL) { + wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot " + "generate msg 2/4"); + return -1; + } + + 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 + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); + os_memcpy(reply + 1, wpa_ie, wpa_ie_len); + + os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); + wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk) +{ +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) + return wpa_derive_ptk_ft(sm, src_addr, key, ptk); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", + sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, + (u8 *) ptk, sizeof(*ptk), + wpa_key_mgmt_sha256(sm->key_mgmt)); + return 0; +} + + +static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + struct wpa_ptk *ptk; + u8 buf[8]; + + if (wpa_sm_get_network_ctx(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); + + os_memset(&ie, 0, sizeof(ie)); + +#ifndef CONFIG_NO_WPA2 + 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); + } + } +#endif /* CONFIG_NO_WPA2 */ + + if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid)) + return; + + if (sm->renew_snonce) { + if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Failed to get random data for SNonce"); + return; + } + sm->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + sm->snonce, WPA_NONCE_LEN); + } + + /* Calculate PTK which will be stored as a temporary PTK until it has + * been verified when processing message 3/4. */ + ptk = &sm->tptk; + wpa_derive_ptk(sm, src_addr, key, ptk); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); + os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); + os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); + sm->tptk_set = 1; + + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, + ptk)) + return; + + os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); +} + + +static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + rsn_preauth_candidate_process(sm); +} + + +static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, + const u8 *addr, int secure) +{ + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Key negotiation completed with " + MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + + if (secure) { + wpa_sm_mlme_setprotection( + sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, TRUE); + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + eapol_sm_notify_eap_success(sm->eapol, TRUE); + /* + * Start preauthentication after a short wait to avoid a + * possible race condition between the data receive and key + * configuration after the 4-Way Handshake. This increases the + * 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; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_sm_key_request(sm, 0, 1); +} + + +static int wpa_supplicant_install_ptk(struct wpa_sm *sm, + const struct wpa_eapol_key *key) +{ + int keylen, rsclen; + wpa_alg alg; + 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 (sm->pairwise_cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + keylen = 16; + rsclen = 6; + break; + case WPA_CIPHER_TKIP: + alg = WPA_ALG_TKIP; + keylen = 32; + rsclen = 6; + break; + case WPA_CIPHER_NONE: + wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + return 0; + default: + wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + if (sm->proto == WPA_PROTO_RSN) { + key_rsc = null_rsc; + } else { + key_rsc = key->key_rsc; + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); + } + + if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, + (u8 *) sm->ptk.tk1, keylen) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " + "driver."); + return -1; + } + + if (sm->wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, + sm, NULL); + } + + return 0; +} + + +static int wpa_supplicant_check_group_cipher(int group_cipher, + int keylen, int maxkeylen, + int *key_rsc_len, wpa_alg *alg) +{ + int ret = 0; + + switch (group_cipher) { + case WPA_CIPHER_CCMP: + if (keylen != 16 || maxkeylen < 16) { + ret = -1; + break; + } + *key_rsc_len = 6; + *alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + if (keylen != 32 || maxkeylen < 32) { + ret = -1; + break; + } + *key_rsc_len = 6; + *alg = WPA_ALG_TKIP; + break; + case WPA_CIPHER_WEP104: + if (keylen != 13 || maxkeylen < 13) { + ret = -1; + break; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP; + break; + case WPA_CIPHER_WEP40: + if (keylen != 5 || maxkeylen < 5) { + ret = -1; + break; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP; + break; + default: + wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", + group_cipher); + return -1; + } + + 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; +} + + +struct wpa_gtk_data { + wpa_alg alg; + int tx, key_rsc_len, keyidx; + u8 gtk[32]; + int gtk_len; +}; + + +static int wpa_supplicant_install_gtk(struct wpa_sm *sm, + const struct wpa_gtk_data *gd, + const u8 *key_rsc) +{ + const u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); + wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " + "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx, + gd->gtk_len); + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + os_memcpy(gtk_buf, gd->gtk, 16); + os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); + os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); + _gtk = gtk_buf; + } + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_sm_set_key(sm, gd->alg, + (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_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; + } + + return 0; +} + + +static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, + int tx) +{ + if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit for GTK if a pairwise key is used. One AP + * seemed to set this bit (incorrectly, since Tx is only when + * doing Group Key only APs) and without this workaround, the + * data connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. */ + wpa_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 struct wpa_eapol_key *key, + const u8 *gtk, size_t gtk_len, + int key_info) +{ +#ifndef CONFIG_NO_WPA2 + struct wpa_gtk_data gd; + + /* + * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x + * GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] + * Reserved [bits 0-7] + * GTK + */ + + os_memset(&gd, 0, sizeof(gd)); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) + return -1; + + gd.keyidx = gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(gtk[0] & BIT(2))); + gtk += 2; + gtk_len -= 2; + + os_memcpy(gd.gtk, gtk, gtk_len); + gd.gtk_len = gtk_len; + + if (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(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + return 0; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + + +static int ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (ie->igtk) { + const struct wpa_igtk_kde *igtk; + u16 keyidx; + if (ie->igtk_len != sizeof(*igtk)) + return -1; + igtk = (const struct wpa_igtk_kde *) ie->igtk; + keyidx = WPA_GET_LE16(igtk->keyid); + wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d " + "pn %02x%02x%02x%02x%02x%02x", + keyidx, MAC2STR(igtk->pn)); + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", + igtk->igtk, WPA_IGTK_LEN); + if (keyidx > 4095) { + wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d", + keyidx); + return -1; + } + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK" + " to the driver"); + return -1; + } + } + + return 0; +#else /* CONFIG_IEEE80211W */ + return 0; +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_report_ie_mismatch(struct wpa_sm *sm, + const char *reason, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +{ + wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + reason, MAC2STR(src_addr)); + + if (sm->ap_wpa_ie) { + wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", + sm->ap_wpa_ie, sm->ap_wpa_ie_len); + } + if (wpa_ie) { + if (!sm->ap_wpa_ie) { + wpa_printf(MSG_INFO, "WPA: No WPA IE in " + "Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", + wpa_ie, wpa_ie_len); + } + + if (sm->ap_rsn_ie) { + wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + } + if (rsn_ie) { + if (!sm->ap_rsn_ie) { + wpa_printf(MSG_INFO, "WPA: No RSN IE in " + "Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", + rsn_ie, rsn_ie_len); + } + + wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); +} + + +static int wpa_supplicant_validate_ie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { + wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " + "Trying to get from scan results"); + if (wpa_sm_get_beacon_ie(sm) < 0) { + wpa_printf(MSG_WARNING, "WPA: Could not find AP from " + "the scan results"); + } else { + wpa_printf(MSG_DEBUG, "WPA: Found the current AP from " + "updated scan results"); + } + } + + if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && + (sm->ap_wpa_ie || sm->ap_rsn_ie)) { + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp (no IE?)", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + + if ((ie->wpa_ie && sm->ap_wpa_ie && + (ie->wpa_ie_len != sm->ap_wpa_ie_len || + os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || + (ie->rsn_ie && sm->ap_rsn_ie && + (ie->rsn_ie_len != sm->ap_rsn_ie_len || + os_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, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + + if (sm->proto == WPA_PROTO_WPA && + ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { + wpa_report_ie_mismatch(sm, "Possible downgrade attack " + "detected - RSN was enabled and RSN IE " + "was in msg 3/4, but not in " + "Beacon/ProbeResp", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + struct rsn_mdie *mdie; + /* TODO: verify that full MDIE matches with the one from scan + * results, not only mobility domain */ + mdie = (struct rsn_mdie *) (ie->mdie + 2); + if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not " + "match with the current mobility domain"); + return -1; + } + } +#endif /* CONFIG_IEEE80211R */ + + return 0; +} + + +/** + * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @key_info: Key Info + * @kde: KDEs to include the EAPOL-Key frame + * @kde_len: Length of KDEs + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info, + const u8 *kde, size_t kde_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + if (kde) + wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply) + kde_len, + &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, kde_len); + if (kde) + os_memcpy(reply + 1, kde, kde_len); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + u16 ver) +{ + u16 key_info, keylen, len; + const u8 *pos; + struct wpa_eapol_ie_parse ie; + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); + + key_info = WPA_GET_BE16(key->key_info); + + pos = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); + 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; + } +#ifdef CONFIG_IEEE80211W + if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key " + "data"); + return; + } + + if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { + wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); + return; + } +#endif /* CONFIG_IEEE80211W */ + + if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) + return; + + if (os_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(sm->bssid)); + return; + } + + 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 " + "%d (src=" MACSTR ")", + keylen, MAC2STR(sm->bssid)); + return; + } + break; + case WPA_CIPHER_TKIP: + if (keylen != 32) { + wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " + "%d (src=" MACSTR ")", + keylen, MAC2STR(sm->bssid)); + return; + } + break; + } + + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + NULL, 0, &sm->ptk)) + return; + + /* SNonce was successfully used in msg 3/4, so mark it to be renewed + * for the next 4-Way Handshake. If msg 3 is received again, the old + * SNonce will still be used to avoid changing PTK. */ + sm->renew_snonce = 1; + + if (key_info & WPA_KEY_INFO_INSTALL) { + wpa_supplicant_install_ptk(sm, key); + } + + if (key_info & WPA_KEY_INFO_SECURE) { + wpa_sm_mlme_setprotection( + sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, TRUE); + } + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (ie.gtk && + wpa_supplicant_pairwise_gtk(sm, key, + ie.gtk, ie.gtk_len, key_info) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); + } + + if (ieee80211w_set_keys(sm, &ie) < 0) + wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); +} + + +static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, + const u8 *keydata, + size_t keydatalen, + u16 key_info, + struct wpa_gtk_data *gd) +{ + int maxkeylen; + struct wpa_eapol_ie_parse ie; + + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + 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; + + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); + gd->keyidx = ie.gtk[0] & 0x3; + gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(ie.gtk[0] & BIT(2))); + if (ie.gtk_len - 2 > sizeof(gd->gtk)) { + wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " + "(len=%lu)", (unsigned long) ie.gtk_len - 2); + return -1; + } + os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); + + if (ieee80211w_set_keys(sm, &ie) < 0) + wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + + return 0; +} + + +static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + size_t keydatalen, int key_info, + size_t extra_len, u16 ver, + struct wpa_gtk_data *gd) +{ + size_t maxkeylen; + u8 ek[32]; + + gd->gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = keydatalen; + if (keydatalen > extra_len) { + wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" + " key_data_length=%lu > extra_len=%lu", + (unsigned long) keydatalen, + (unsigned long) extra_len); + return -1; + } + if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (maxkeylen < 8) { + wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); + return -1; + } + maxkeylen -= 8; + } + + 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) { + os_memcpy(ek, key->key_iv, 16); + os_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; + } + os_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; + } + } else { + wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", + ver); + return -1; + } + gd->tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(key_info & WPA_KEY_INFO_TXRX)); + return 0; +} + + +static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + int ver, u16 key_info) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; + key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int extra_len, u16 ver) +{ + u16 key_info, keydatalen; + int rekey, ret; + struct wpa_gtk_data gd; + + os_memset(&gd, 0, sizeof(gd)); + + rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; + wpa_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) { + ret = wpa_supplicant_process_1_of_2_rsn(sm, + (const u8 *) (key + 1), + keydatalen, key_info, + &gd); + } else { + ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, + key_info, extra_len, + ver, &gd); + } + + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (ret) + return; + + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) + return; + + if (rekey) { + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying " + "completed with " MACSTR " [GTK=%s]", + MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + } else { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & + WPA_KEY_INFO_SECURE); + } +} + + +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_eapol_key *key, + u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + os_memcpy(mic, key->key_mic, 16); + if (sm->tptk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); + } else { + ok = 1; + sm->tptk_set = 0; + sm->ptk_set = 1; + os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + } + } + + if (!ok && sm->ptk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + + os_memcpy(sm->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + return 0; +} + + +/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ +static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, + struct wpa_eapol_key *key, u16 ver) +{ + u16 keydatalen = WPA_GET_BE16(key->key_data_length); + + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + (u8 *) (key + 1), keydatalen); + if (!sm->ptk_set) { + wpa_printf(MSG_WARNING, "WPA: PTK not available, " + "cannot decrypt EAPOL-Key key data."); + return -1; + } + + /* Decrypt key data here so that this operation does not need + * to be implemented separately for each message type. */ + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + u8 ek[32]; + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, 16); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + u8 *buf; + if (keydatalen % 8) { + wpa_printf(MSG_WARNING, "WPA: Unsupported " + "AES-WRAP len %d", keydatalen); + return -1; + } + keydatalen -= 8; /* AES-WRAP adds 8 bytes */ + buf = os_malloc(keydatalen); + if (buf == NULL) { + wpa_printf(MSG_WARNING, "WPA: No memory for " + "AES-UNWRAP buffer"); + return -1; + } + if (aes_unwrap(sm->ptk.kek, keydatalen / 8, + (u8 *) (key + 1), buf)) { + os_free(buf); + wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); + return -1; + } + os_memcpy(key + 1, buf, keydatalen); + os_free(buf); + WPA_PUT_BE16(key->key_data_length, keydatalen); + } else { + wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", + ver); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", + (u8 *) (key + 1), keydatalen); + return 0; +} + + +/** + * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_aborted_cached(struct wpa_sm *sm) +{ + if (sm && sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); + sm->cur_pmksa = NULL; + } +} + + +static void wpa_eapol_key_dump(const struct wpa_eapol_key *key) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + u16 key_info = WPA_GET_BE16(key->key_info); + + wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type); + wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s" + "%s%s%s%s%s%s%s)", + key_info, key_info & WPA_KEY_INFO_TYPE_MASK, + (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT, + (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, + key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", + key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", + key_info & WPA_KEY_INFO_ACK ? " Ack" : "", + key_info & WPA_KEY_INFO_MIC ? " MIC" : "", + key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", + key_info & WPA_KEY_INFO_ERROR ? " Error" : "", + key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", + key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); + wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u", + WPA_GET_BE16(key->key_length), + WPA_GET_BE16(key->key_data_length)); + wpa_hexdump(MSG_DEBUG, " replay_counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16); + wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8); + wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8); + wpa_hexdump(MSG_DEBUG, " key_mic", key->key_mic, 16); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +/** + * wpa_sm_rx_eapol - Process received WPA EAPOL frames + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @src_addr: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure + * + * This function is called for each received EAPOL frame. Other than EAPOL-Key + * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is + * only processing WPA and WPA2 EAPOL-Key frames. + * + * The received EAPOL-Key packets are validated and valid packets are replied + * to. In addition, key material (PTK, GTK) is configured at the end of a + * successful key handshake. + */ +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + size_t plen, data_len, extra_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, ver; + u8 *tmp; + int ret = -1; + struct wpa_peerkey *peerkey = NULL; + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + + if (len < sizeof(*hdr) + sizeof(*key)) { + wpa_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 0; + } + + tmp = os_malloc(len); + if (tmp == NULL) + return -1; + os_memcpy(tmp, buf, len); + + hdr = (struct ieee802_1x_hdr *) tmp; + key = (struct wpa_eapol_key *) (hdr + 1); + plen = be_to_host16(hdr->length); + data_len = plen + sizeof(*hdr); + wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", + hdr->version, hdr->type, (unsigned long) plen); + + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " + "not a Key frame", hdr->type); + ret = 0; + goto out; + } + if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); + ret = 0; + goto out; + } + + if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) + { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " + "discarded", key->type); + ret = 0; + goto out; + } + wpa_eapol_key_dump(key); + + eapol_sm_notify_lower_layer_success(sm->eapol, 0); + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); + if (data_len < len) { + wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " + "802.1X data", (unsigned long) len - data_len); + } + key_info = WPA_GET_BE16(key->key_info); + ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && +#ifdef CONFIG_IEEE80211R + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && +#endif /* CONFIG_IEEE80211R */ + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " + "version %d.", ver); + goto out; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_printf(MSG_INFO, "FT: AP did not use " + "AES-128-CMAC."); + goto out; + } + } else +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_printf(MSG_INFO, "WPA: AP did not use the " + "negotiated AES-128-CMAC."); + goto out; + } + } else +#endif /* CONFIG_IEEE80211W */ + if (sm->pairwise_cipher == WPA_CIPHER_CCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2.", ver); + if (sm->group_cipher != WPA_CIPHER_CCMP && + !(key_info & WPA_KEY_INFO_KEY_TYPE)) { + /* Earlier versions of IEEE 802.11i did not explicitly + * require version 2 descriptor for all EAPOL-Key + * packets, so allow group keys to use version 1 if + * CCMP is not used for them. */ + wpa_printf(MSG_INFO, "WPA: Backwards compatibility: " + "allow invalid version for non-CCMP group " + "keys"); + } else + goto out; + } + +#ifdef CONFIG_PEERKEY + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { + if (!peerkey->initiator && peerkey->replay_counter_set && + os_memcmp(key->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay " + "Counter did not increase (STK) - dropping " + "packet"); + goto out; + } else if (peerkey->initiator) { + u8 _tmp[WPA_REPLAY_COUNTER_LEN]; + os_memcpy(_tmp, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); + if (os_memcmp(_tmp, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) != 0) { + wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay " + "Counter did not match (STK) - " + "dropping packet"); + goto out; + } + } + } + + if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { + wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer"); + goto out; + } +#endif /* CONFIG_PEERKEY */ + + if (!peerkey && sm->rx_replay_counter_set && + os_memcmp(key->replay_counter, sm->rx_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not" + " increase - dropping packet"); + goto out; + } + + if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) +#ifdef CONFIG_PEERKEY + && (peerkey == NULL || !peerkey->initiator) +#endif /* CONFIG_PEERKEY */ + ) { + wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); + goto out; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - " + "dropped"); + goto out; + } + + if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + goto out; + +#ifdef CONFIG_PEERKEY + if ((key_info & WPA_KEY_INFO_MIC) && peerkey && + peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len)) + goto out; +#endif /* CONFIG_PEERKEY */ + + extra_len = data_len - sizeof(*hdr) - sizeof(*key); + + if (WPA_GET_BE16(key->key_data_length) > extra_len) { + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%d > %lu)", + WPA_GET_BE16(key->key_data_length), + (unsigned long) extra_len); + goto out; + } + extra_len = WPA_GET_BE16(key->key_data_length); + + if (sm->proto == WPA_PROTO_RSN && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supplicant_decrypt_key_data(sm, key, ver)) + goto out; + extra_len = WPA_GET_BE16(key->key_data_length); + } + + if (key_info & WPA_KEY_INFO_KEY_TYPE) { + if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { + wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key " + "(Pairwise) with non-zero key index"); + goto out; + } + if (peerkey) { + /* PeerKey 4-Way Handshake */ + peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver); + } else if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(sm, key, ver); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(sm, src_addr, key, + ver); + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + /* PeerKey SMK Handshake */ + peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info, + ver); + } else { + if (key_info & WPA_KEY_INFO_MIC) { + /* 1/2 Group Key Handshake */ + wpa_supplicant_process_1_of_2(sm, src_addr, key, + extra_len, ver); + } else { + wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) " + "without Mic bit - dropped"); + } + } + + ret = 1; + +out: + os_free(tmp); + return ret; +} + + +#ifdef CONFIG_CTRL_IFACE +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) +{ + switch (sm->key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : + WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + case WPA_KEY_MGMT_PSK: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : + WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return RSN_AUTH_KEY_MGMT_FT_802_1X; + case WPA_KEY_MGMT_FT_PSK: + return RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; + case WPA_KEY_MGMT_PSK_SHA256: + return RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_WPA_NONE: + return WPA_AUTH_KEY_MGMT_NONE; + default: + return 0; + } +} + + +static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return (sm->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + case WPA_CIPHER_TKIP: + return (sm->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + case WPA_CIPHER_WEP104: + return (sm->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + case WPA_CIPHER_WEP40: + return (sm->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + case WPA_CIPHER_NONE: + return (sm->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + default: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +/** + * wpa_sm_get_mib - Dump text list of MIB entries + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for the list + * @buflen: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used fetch dot11 MIB variables. + */ +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + char pmkid_txt[PMKID_LEN * 2 + 1]; + int rsna, ret; + size_t len; + + if (sm->cur_pmksa) { + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + sm->cur_pmksa->pmkid, PMKID_LEN); + } else + pmkid_txt[0] = '\0'; + + if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && + sm->proto == WPA_PROTO_RSN) + rsna = 1; + else + rsna = 0; + + ret = os_snprintf(buf, buflen, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=TRUE\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n" + "dot11RSNAConfigVersion=%d\n" + "dot11RSNAConfigPairwiseKeysSupported=5\n" + "dot11RSNAConfigGroupCipherSize=%d\n" + "dot11RSNAConfigPMKLifetime=%d\n" + "dot11RSNAConfigPMKReauthThreshold=%d\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" + "dot11RSNAConfigSATimeout=%d\n", + rsna ? "TRUE" : "FALSE", + rsna ? "TRUE" : "FALSE", + RSN_VERSION, + wpa_cipher_bits(sm->group_cipher), + sm->dot11RSNAConfigPMKLifetime, + sm->dot11RSNAConfigPMKReauthThreshold, + sm->dot11RSNAConfigSATimeout); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + len = ret; + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" + "dot11RSNA4WayHandshakeFailures=%u\n", + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + pmkid_txt, + 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); + if (ret >= 0 && (size_t) ret < buflen) + len += ret; + + return (int) len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx, int replace) +{ + struct wpa_sm *sm = ctx; + + if (sm->cur_pmksa == entry || + (sm->pmk_len == entry->pmk_len && + os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { + 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; + } + + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); + } +} + + +/** + * wpa_sm_init - Initialize WPA state machine + * @ctx: Context pointer for callbacks; this needs to be an allocated buffer + * Returns: Pointer to the allocated WPA state machine data + * + * This function is used to allocate a new WPA state machine and the returned + * value is passed to all WPA state machine calls. + */ +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + struct wpa_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->renew_snonce = 1; + sm->ctx = ctx; + + sm->dot11RSNAConfigPMKLifetime = 43200; + sm->dot11RSNAConfigPMKReauthThreshold = 70; + sm->dot11RSNAConfigSATimeout = 60; + + sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); + if (sm->pmksa == NULL) { + wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization " + "failed"); + os_free(sm); + return NULL; + } + + return sm; +} + + +/** + * wpa_sm_deinit - Deinitialize WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + pmksa_cache_deinit(sm->pmksa); + eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + os_free(sm->assoc_wpa_ie); + os_free(sm->ap_wpa_ie); + os_free(sm->ap_rsn_ie); + os_free(sm->ctx); + peerkey_deinit(sm); + os_free(sm); +} + + +/** + * wpa_sm_notify_assoc - Notify WPA state machine about association + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: The BSSID of the new association + * + * This function is called to let WPA state machine know that the connection + * was established. + */ +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ + int clear_ptk = 1; + + if (sm == NULL) + return; + + wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter"); + os_memcpy(sm->bssid, bssid, ETH_ALEN); + os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 0; + sm->renew_snonce = 1; + if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) + rsn_preauth_deinit(sm); + +#ifdef CONFIG_IEEE80211R + if (wpa_ft_is_completed(sm)) { + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm); + + clear_ptk = 0; + } +#endif /* CONFIG_IEEE80211R */ + + if (clear_ptk) { + /* + * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if + * this is not part of a Fast BSS Transition. + */ + wpa_printf(MSG_DEBUG, "WPA: Clear old PTK"); + sm->ptk_set = 0; + sm->tptk_set = 0; + } +} + + +/** + * 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) +{ + rsn_preauth_deinit(sm); + if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) + sm->dot11RSNA4WayHandshakeFailures++; +} + + +/** + * wpa_sm_set_pmk - Set PMK + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmk: The new PMK + * @pmk_len: The length of the new PMK in bytes + * + * Configure the PMK for WPA state machine. + */ +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) +{ + if (sm == NULL) + return; + + sm->pmk_len = pmk_len; + os_memcpy(sm->pmk, pmk, pmk_len); + +#ifdef CONFIG_IEEE80211R + /* Set XXKey to be PSK for FT key derivation */ + sm->xxkey_len = pmk_len; + os_memcpy(sm->xxkey, pmk, pmk_len); +#endif /* CONFIG_IEEE80211R */ +} + + +/** + * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK + * will be cleared. + */ +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->cur_pmksa) { + sm->pmk_len = sm->cur_pmksa->pmk_len; + os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); + } else { + sm->pmk_len = PMK_LEN; + os_memset(sm->pmk, 0, PMK_LEN); + } +} + + +/** + * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @fast_reauth: Whether fast reauthentication (EAP) is allowed + */ +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ + if (sm) + sm->fast_reauth = fast_reauth; +} + + +/** + * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @scard_ctx: Context pointer for smartcard related callback functions + */ +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) +{ + if (sm == NULL) + return; + sm->scard_ctx = scard_ctx; + if (sm->preauth_eapol) + eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); +} + + +/** + * wpa_sm_set_config - Notification of current configration change + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @config: Pointer to current network configuration + * + * Notify WPA state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. + */ +void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) +{ + if (!sm) + return; + + if (config) { + sm->network_ctx = config->network_ctx; + sm->peerkey_enabled = config->peerkey_enabled; + sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; + sm->proactive_key_caching = config->proactive_key_caching; + sm->eap_workaround = config->eap_workaround; + sm->eap_conf_ctx = config->eap_conf_ctx; + if (config->ssid) { + os_memcpy(sm->ssid, config->ssid, config->ssid_len); + sm->ssid_len = config->ssid_len; + } else + sm->ssid_len = 0; + sm->wpa_ptk_rekey = config->wpa_ptk_rekey; + } else { + sm->network_ctx = NULL; + sm->peerkey_enabled = 0; + sm->allowed_pairwise_cipher = 0; + sm->proactive_key_caching = 0; + sm->eap_workaround = 0; + sm->eap_conf_ctx = NULL; + sm->ssid_len = 0; + sm->wpa_ptk_rekey = 0; + } + if (config == NULL || config->network_ctx != sm->network_ctx) + pmksa_cache_notify_reconfig(sm->pmksa); +} + + +/** + * wpa_sm_set_own_addr - Set own MAC address + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @addr: Own MAC address + */ +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ + if (sm) + os_memcpy(sm->own_addr, addr, ETH_ALEN); +} + + +/** + * wpa_sm_set_ifname - Set network interface name + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ifname: Interface name + * @bridge_ifname: Optional bridge interface name (for pre-auth) + */ +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname) +{ + if (sm) { + sm->ifname = ifname; + sm->bridge_ifname = bridge_ifname; + } +} + + +/** + * wpa_sm_set_eapol - Set EAPOL state machine pointer + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ + if (sm) + sm->eapol = eapol; +} + + +/** + * wpa_sm_set_param - Set WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * @value: Parameter value + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value) +{ + int ret = 0; + + if (sm == NULL) + return -1; + + switch (param) { + case RSNA_PMK_LIFETIME: + if (value > 0) + sm->dot11RSNAConfigPMKLifetime = value; + else + ret = -1; + break; + case RSNA_PMK_REAUTH_THRESHOLD: + if (value > 0 && value <= 100) + sm->dot11RSNAConfigPMKReauthThreshold = value; + else + ret = -1; + break; + case RSNA_SA_TIMEOUT: + if (value > 0) + sm->dot11RSNAConfigSATimeout = value; + else + ret = -1; + break; + case WPA_PARAM_PROTO: + sm->proto = value; + break; + case WPA_PARAM_PAIRWISE: + sm->pairwise_cipher = value; + break; + case WPA_PARAM_GROUP: + sm->group_cipher = value; + break; + case WPA_PARAM_KEY_MGMT: + sm->key_mgmt = value; + break; +#ifdef CONFIG_IEEE80211W + case WPA_PARAM_MGMT_GROUP: + sm->mgmt_group_cipher = value; + break; +#endif /* CONFIG_IEEE80211W */ + case WPA_PARAM_RSN_ENABLED: + sm->rsn_enabled = value; + break; + default: + break; + } + + return ret; +} + + +/** + * wpa_sm_get_param - Get WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * Returns: Parameter value + */ +unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) +{ + if (sm == NULL) + return 0; + + switch (param) { + case RSNA_PMK_LIFETIME: + return sm->dot11RSNAConfigPMKLifetime; + case RSNA_PMK_REAUTH_THRESHOLD: + return sm->dot11RSNAConfigPMKReauthThreshold; + case RSNA_SA_TIMEOUT: + return sm->dot11RSNAConfigSATimeout; + case WPA_PARAM_PROTO: + return sm->proto; + case WPA_PARAM_PAIRWISE: + return sm->pairwise_cipher; + case WPA_PARAM_GROUP: + return sm->group_cipher; + case WPA_PARAM_KEY_MGMT: + return sm->key_mgmt; +#ifdef CONFIG_IEEE80211W + case WPA_PARAM_MGMT_GROUP: + return sm->mgmt_group_cipher; +#endif /* CONFIG_IEEE80211W */ + case WPA_PARAM_RSN_ENABLED: + return sm->rsn_enabled; + default: + return 0; + } +} + + +/** + * wpa_sm_get_status - Get WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA state machine for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int ret; + + ret = os_snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n", + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher), + wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + return pos - buf; +} + + +/** + * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to buffer for WPA/RSN IE + * @wpa_ie_len: Pointer to the length of the wpa_ie buffer + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len) +{ + int res; + + if (sm == NULL) + return -1; + + res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); + if (res < 0) + return -1; + *wpa_ie_len = res; + + wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", + wpa_ie, *wpa_ie_len); + + if (sm->assoc_wpa_ie == NULL) { + /* + * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets + * the correct version of the IE even if PMKSA caching is + * aborted (which would remove PMKID from IE generation). + */ + sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); + sm->assoc_wpa_ie_len = *wpa_ie_len; + } + + return 0; +} + + +/** + * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association + * Request frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_wpa_ie_default(). + */ +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->assoc_wpa_ie); + if (ie == NULL || len == 0) { + wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); + sm->assoc_wpa_ie = NULL; + sm->assoc_wpa_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); + sm->assoc_wpa_ie = os_malloc(len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, ie, len); + sm->assoc_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_wpa_ie); + if (ie == NULL || len == 0) { + wpa_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 = os_malloc(len); + if (sm->ap_wpa_ie == NULL) + return -1; + + os_memcpy(sm->ap_wpa_ie, ie, len); + sm->ap_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the RSN IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_rsn_ie); + if (ie == NULL || len == 0) { + wpa_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 = os_malloc(len); + if (sm->ap_rsn_ie == NULL) + return -1; + + os_memcpy(sm->ap_rsn_ie, ie, len); + sm->ap_rsn_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure + * + * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the + * parsed data into data. + */ +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) +{ + if (sm == NULL || 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/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h new file mode 100644 index 0000000000000..bdf778544f416 --- /dev/null +++ b/src/rsn_supp/wpa.h @@ -0,0 +1,320 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#include "defs.h" +#include "eapol_common.h" +#include "wpa_common.h" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif + +struct wpa_sm; +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 (*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 * (*get_network_ctx)(void *ctx); + int (*get_bssid)(void *ctx, u8 *bssid); + int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len); + int (*get_beacon_ie)(void *ctx); + void (*cancel_auth_timeout)(void *ctx); + u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, + size_t *msg_len, void **data_pos); + int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + int (*mlme_setprotection)(void *ctx, const u8 *addr, + int protection_type, int key_type); + int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies, + size_t ies_len); + int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len); +}; + + +enum wpa_sm_conf_params { + RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */, + RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */, + RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */, + WPA_PARAM_PROTO, + WPA_PARAM_PAIRWISE, + WPA_PARAM_GROUP, + WPA_PARAM_KEY_MGMT, + WPA_PARAM_MGMT_GROUP, + WPA_PARAM_RSN_ENABLED +}; + +struct rsn_supp_config { + void *network_ctx; + int peerkey_enabled; + int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ + int proactive_key_caching; + int eap_workaround; + void *eap_conf_ctx; + const u8 *ssid; + size_t ssid_len; + int wpa_ptk_rekey; +}; + +#ifndef CONFIG_NO_WPA + +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); +void wpa_sm_deinit(struct wpa_sm *sm); +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); +void wpa_sm_notify_disassoc(struct wpa_sm *sm); +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); +void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config); +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr); +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname); +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len); +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); + +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value); +unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param); + +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); + +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); + +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); + +void wpa_sm_aborted_cached(struct wpa_sm *sm); +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); + +#else /* CONFIG_NO_WPA */ + +static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + return (struct wpa_sm *) 1; +} + +static inline void wpa_sm_deinit(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ +} + +static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, + size_t pmk_len) +{ +} + +static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ +} + +static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) +{ +} + +static inline void wpa_sm_set_config(struct wpa_sm *sm, + struct rsn_supp_config *config) +{ +} + +static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ +} + +static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname) +{ +} + +static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ +} + +static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, + u8 *wpa_ie, + size_t *wpa_ie_len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + return 0; +} + +static inline int wpa_sm_set_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param, + unsigned int value) +{ + return -1; +} + +static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param) +{ + return 0; +} + +static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, + int pairwise) +{ +} + +static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline void wpa_sm_aborted_cached(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, + struct wpa_ie_data *data) +{ + return -1; +} + +#endif /* CONFIG_NO_WPA */ + +#ifdef CONFIG_PEERKEY +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); +#else /* CONFIG_PEERKEY */ +static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + return -1; +} +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R + +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, + const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *r1kh_id); +int wpa_ft_prepare_auth_request(struct wpa_sm *sm); +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap); +int wpa_ft_is_completed(struct wpa_sm *sm); +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr); +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap); + +#else /* CONFIG_IEEE80211R */ + +static inline int +wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, + const u8 *r0kh_id, const u8 *r1kh_id) +{ + return 0; +} + +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm) +{ + return 0; +} + +static inline int +wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap) +{ + return 0; +} + +static inline int wpa_ft_is_completed(struct wpa_sm *sm) +{ + return 0; +} + +static inline int +wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + const u8 *src_addr) +{ + return -1; +} + +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c new file mode 100644 index 0000000000000..c89b89a772dfb --- /dev/null +++ b/src/rsn_supp/wpa_ft.c @@ -0,0 +1,871 @@ +/* + * WPA Supplicant - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "aes_wrap.h" +#include "ieee802_11_defs.h" + +#ifdef CONFIG_IEEE80211R + +int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk) +{ + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *anonce = key->key_nonce; + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", + sm->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, + sm->own_addr, sm->pmk_r1, pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, + sm->bssid, pmk_r1_name, + (u8 *) ptk, sizeof(*ptk), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +/** + * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @mobility_domain: Mobility domain identifier (2 octets) + * @r0kh_id: PMK-R0 key holder identity (1-48 octets) + * @r0kh_id_len: R0KH-ID length (1-48) + * @r1kh_id: PMK-R1 key holder identity (16 octets) + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, + const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *r1kh_id) +{ + if (sm && mobility_domain) { + wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", + mobility_domain, MOBILITY_DOMAIN_ID_LEN); + os_memcpy(sm->mobility_domain, mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + } else if (sm) + os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); + + if (sm && r0kh_id) { + if (r0kh_id_len > FT_R0KH_ID_MAX_LEN) + return -1; + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len); + os_memcpy(sm->r0kh_id, r0kh_id, r0kh_id_len); + sm->r0kh_id_len = r0kh_id_len; + } else if (sm) { + /* FIX: When should R0KH-ID be cleared? We need to keep the + * old R0KH-ID in order to be able to use this during FT. */ + /* + * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); + * sm->r0kh_id_len = 0; + */ + } + + if (sm && r1kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(sm->r1kh_id, r1kh_id, FT_R1KH_ID_LEN); + } else if (sm) + os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); + + return 0; +} + + +/** + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @len: Buffer for returning the length of the IEs + * @anonce: ANonce or %NULL if not yet available + * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List + * @kck: 128-bit KCK for MIC or %NULL if no MIC is used + * @target_ap: Target AP address + * Returns: Pointer to buffer with IEs or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(); + */ +static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, + const u8 *anonce, const u8 *pmk_name, + const u8 *kck, const u8 *target_ap) +{ + size_t buf_len; + u8 *buf, *pos, *ftie_len, *ftie_pos; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + struct rsn_ie_hdr *rsnie; + u16 capab; + + sm->ft_completed = 0; + + buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + sm->r0kh_id_len + 100; + buf = os_zalloc(buf_len); + if (buf == NULL) + return NULL; + pos = buf; + + /* RSNIE[PMKR0Name] */ + rsnie = (struct rsn_ie_hdr *) pos; + rsnie->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(rsnie->version, RSN_VERSION); + pos = (u8 *) (rsnie + 1); + + /* Group Suite Selector */ + if (sm->group_cipher == WPA_CIPHER_CCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (sm->group_cipher == WPA_CIPHER_TKIP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + else { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + os_free(buf); + return NULL; + } + pos += RSN_SELECTOR_LEN; + + /* Pairwise Suite Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Pairwise Suite List */ + if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + else if (sm->pairwise_cipher == WPA_CIPHER_TKIP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + else { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + os_free(buf); + return NULL; + } + pos += RSN_SELECTOR_LEN; + + /* Authenticated Key Management Suite Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Authenticated Key Management Suite List */ + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + else { + wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", + sm->key_mgmt); + os_free(buf); + return NULL; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* PMKID List [PMKR0Name/PMKR1Name] */ + os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); + pos += WPA_PMK_NAME_LEN; + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + rsnie->len = (pos - (u8 *) rsnie) - 2; + + /* MDIE */ + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(*mdie); + mdie = (struct rsn_mdie *) pos; + pos += sizeof(*mdie); + os_memcpy(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = 0; /* FIX: copy from the target AP's MDIE */ + + /* FTIE[SNonce, R0KH-ID] */ + ftie_pos = pos; + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ftie_len = pos++; + ftie = (struct rsn_ftie *) pos; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + /* R0KH-ID sub-element */ + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = sm->r0kh_id_len; + os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); + pos += sm->r0kh_id_len; + *ftie_len = pos - ftie_len - 1; + + if (kck) { + /* + * IEEE Std 802.11r-2008, 11A.8.4 + * MIC shall be calculated over: + * non-AP STA MAC address + * Target AP MAC address + * Transaction seq number (5 for ReassocReq, 3 otherwise) + * RSN IE + * MDIE + * FTIE (with MIC field set to 0) + * RIC-Request (if present) + */ + ftie->mic_control[1] = 3; /* Information element count */ + if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, + ((u8 *) mdie) - 2, 2 + sizeof(*mdie), + ftie_pos, 2 + *ftie_len, + (u8 *) rsnie, 2 + rsnie->len, NULL, 0, + ftie->mic) < 0) { + wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); + os_free(buf); + return NULL; + } + } + + *len = pos - buf; + + return buf; +} + + +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *tie; + size_t tie_len; + const u8 *igtk; + size_t igtk_len; +}; + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; +#ifdef CONFIG_IEEE80211W + case FTIE_SUBELEM_IGTK: + parse->igtk = pos + 2; + parse->igtk_len = pos[1]; + break; +#endif /* CONFIG_IEEE80211W */ + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + parse->tie = pos + 2; + parse->tie_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) +{ + int keylen; + wpa_alg alg; + u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; + + wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); + + switch (sm->pairwise_cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + keylen = 16; + break; + case WPA_CIPHER_TKIP: + alg = WPA_ALG_TKIP; + keylen = 32; + break; + default: + wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, + sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) { + wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); + return -1; + } + + return 0; +} + + +/** + * wpa_ft_prepare_auth_request - Generate over-the-air auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_prepare_auth_request(struct wpa_sm *sm) +{ + u8 *ft_ies; + size_t ft_ies_len; + + /* Generate a new SNonce */ + if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); + return -1; + } + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, + NULL, sm->bssid); + if (ft_ies) { + wpa_sm_update_ft_ies(sm, sm->mobility_domain, + ft_ies, ft_ies_len); + os_free(ft_ies); + } + + return 0; +} + + +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap) +{ + u8 *ft_ies; + size_t ft_ies_len; + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 ptk_name[WPA_PMK_NAME_LEN]; + int ret; + const u8 *bssid; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + + if (ft_action) { + if (!sm->over_the_ds_in_progress) { + wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " + "- drop FT Action Response"); + return -1; + } + + if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " + "with this Target AP - drop FT Action " + "Response"); + return -1; + } + } + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); + return -1; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return -1; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " + "RSNIE"); + return -1; + } + + os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); + wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, + sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + + bssid = target_ap; + wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, + bssid, sm->pmk_r1_name, + (u8 *) &sm->ptk, sizeof(sm->ptk), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->ptk, sizeof(sm->ptk)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + sm->pmk_r1_name, sm->ptk.kck, bssid); + if (ft_ies) { + wpa_sm_update_ft_ies(sm, sm->mobility_domain, + ft_ies, ft_ies_len); + os_free(ft_ies); + } + + ret = wpa_ft_install_ptk(sm, bssid); + + if (ret == 0) { + sm->ft_completed = 1; + if (ft_action) { + /* TODO: trigger re-association to the Target AP; + * MLME is now doing this automatically, but it should + * really be done only if we get here successfully. */ + os_memcpy(sm->bssid, target_ap, ETH_ALEN); + } + } + + return ret; +} + + +int wpa_ft_is_completed(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) + return 0; + + return sm->ft_completed; +} + + +static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, + size_t gtk_elem_len) +{ + u8 gtk[32]; + int keyidx; + wpa_alg alg; + size_t gtk_len, keylen, rsc_len; + + if (gtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", + gtk_elem, gtk_elem_len); + + if (gtk_elem_len < 10 + 24 || (gtk_elem_len - 10) % 8 || + gtk_elem_len - 18 > sizeof(gtk)) { + wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " + "length %lu", (unsigned long) gtk_elem_len); + return -1; + } + gtk_len = gtk_elem_len - 18; + if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 10, gtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt GTK"); + return -1; + } + + switch (sm->group_cipher) { + case WPA_CIPHER_CCMP: + keylen = 16; + rsc_len = 6; + alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + keylen = 32; + rsc_len = 6; + alg = WPA_ALG_TKIP; + break; + case WPA_CIPHER_WEP104: + keylen = 13; + rsc_len = 0; + alg = WPA_ALG_WEP; + break; + case WPA_CIPHER_WEP40: + keylen = 5; + rsc_len = 0; + alg = WPA_ALG_WEP; + break; + default: + wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", + sm->group_cipher); + return -1; + } + + if (gtk_len < keylen) { + wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); + return -1; + } + + /* Key Info[1] | Key Length[1] | RSC[8] | Key[5..32]. */ + + keyidx = gtk_elem[0] & 0x03; + + if (gtk_elem[1] != keylen) { + wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " + "negotiated %lu", + gtk_elem[1], (unsigned long) keylen); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, 0, gtk_elem + 2, rsc_len, gtk, keylen) < + 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " + "driver."); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211W +static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, + size_t igtk_elem_len) +{ + u8 igtk[WPA_IGTK_LEN]; + u16 keyidx; + + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (igtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", + igtk_elem, igtk_elem_len); + + if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " + "length %lu", (unsigned long) igtk_elem_len); + return -1; + } + if (igtk_elem[8] != WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " + "%d", igtk_elem[8]); + return -1; + } + + if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt IGTK"); + return -1; + } + + /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ + + keyidx = WPA_GET_LE16(igtk_elem); + + wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, + WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < + 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " + "driver."); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + size_t count; + u8 mic[16]; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); + return -1; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return -1; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " + "ReassocResp"); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); + return -1; + } + + count = 3; + if (parse.tie) + count++; + + if (ftie->mic_control[1] != count) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", + ftie->mic_control[1]); + return -1; + } + + if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, NULL, 0, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return -1; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return -1; + } + + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) + return -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_ft_start_over_ds - Generate over-the-DS auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap) +{ + u8 *ft_ies; + size_t ft_ies_len; + + wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, + MAC2STR(target_ap)); + + /* Generate a new SNonce */ + if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); + return -1; + } + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, + NULL, target_ap); + if (ft_ies) { + sm->over_the_ds_in_progress = 1; + os_memcpy(sm->target_ap, target_ap, ETH_ALEN); + wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); + os_free(ft_ies); + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h new file mode 100644 index 0000000000000..95348da8a9960 --- /dev/null +++ b/src/rsn_supp/wpa_i.h @@ -0,0 +1,245 @@ +/* + * wpa_supplicant - Internal WPA state machine definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_I_H +#define WPA_I_H + +struct rsn_pmksa_candidate; +struct wpa_peerkey; +struct wpa_eapol_key; + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + int renew_snonce; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + + struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ + + struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ + struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ + struct rsn_pmksa_candidate *pmksa_candidates; + + struct l2_packet_data *l2_preauth; + struct l2_packet_data *l2_preauth_br; + u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or + * 00:00:00:00:00:00 if no pre-auth is + * in progress */ + struct eapol_sm *preauth_eapol; + + struct wpa_sm_ctx *ctx; + + void *scard_ctx; /* context for smartcard callbacks */ + int fast_reauth; /* whether EAP fast re-authentication is enabled */ + + void *network_ctx; + int peerkey_enabled; + int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ + int proactive_key_caching; + int eap_workaround; + void *eap_conf_ctx; + u8 ssid[32]; + size_t ssid_len; + int wpa_ptk_rekey; + + u8 own_addr[ETH_ALEN]; + const char *ifname; + const char *bridge_ifname; + u8 bssid[ETH_ALEN]; + + unsigned int dot11RSNAConfigPMKLifetime; + unsigned int dot11RSNAConfigPMKReauthThreshold; + unsigned int dot11RSNAConfigSATimeout; + + unsigned int dot11RSNA4WayHandshakeFailures; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + unsigned int proto; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; + unsigned int mgmt_group_cipher; + + int rsn_enabled; /* Whether RSN is enabled in configuration */ + + u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ + size_t assoc_wpa_ie_len; + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; + +#ifdef CONFIG_PEERKEY + struct wpa_peerkey *peerkey; +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; + size_t r0kh_id_len; + u8 r1kh_id[FT_R1KH_ID_LEN]; + int ft_completed; + int over_the_ds_in_progress; + u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ +#endif /* CONFIG_IEEE80211R */ +}; + + +static inline void wpa_sm_set_state(struct wpa_sm *sm, wpa_states state) +{ + WPA_ASSERT(sm->ctx->set_state); + sm->ctx->set_state(sm->ctx->ctx, state); +} + +static inline wpa_states wpa_sm_get_state(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_state); + return sm->ctx->get_state(sm->ctx->ctx); +} + +static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) +{ + WPA_ASSERT(sm->ctx->deauthenticate); + sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); +} + +static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) +{ + WPA_ASSERT(sm->ctx->disassociate); + 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) +{ + WPA_ASSERT(sm->ctx->set_key); + return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); +} + +static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_network_ctx); + return sm->ctx->get_network_ctx(sm->ctx->ctx); +} + +static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) +{ + WPA_ASSERT(sm->ctx->get_bssid); + return sm->ctx->get_bssid(sm->ctx->ctx, bssid); +} + +static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + WPA_ASSERT(sm->ctx->ether_send); + return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); +} + +static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_beacon_ie); + return sm->ctx->get_beacon_ie(sm->ctx->ctx); +} + +static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->cancel_auth_timeout); + sm->ctx->cancel_auth_timeout(sm->ctx->ctx); +} + +static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + WPA_ASSERT(sm->ctx->alloc_eapol); + return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, + msg_len, data_pos); +} + +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->add_pmkid); + return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->remove_pmkid); + return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, + int protect_type, int key_type) +{ + WPA_ASSERT(sm->ctx->mlme_setprotection); + return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type, + key_type); +} + +static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->update_ft_ies) + return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len); + return -1; +} + +static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->send_ft_action) + return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap, + ies, ies_len); + return -1; +} + + +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic); +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + int ver, const u8 *nonce, + const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ptk *ptk); +int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info, + const u8 *kde, size_t kde_len, + struct wpa_ptk *ptk); + +int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk); + +#endif /* WPA_I_H */ diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c new file mode 100644 index 0000000000000..84f2811b88619 --- /dev/null +++ b/src/rsn_supp/wpa_ie.c @@ -0,0 +1,536 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE processing + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "pmksa_cache.h" +#include "ieee802_11_defs.h" +#include "wpa_i.h" +#include "wpa_ie.h" + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_WPA; + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -1; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -1; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -1; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -1; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + else + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); +} + + +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos; + struct wpa_ie_hdr *hdr; + + if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (group_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (group_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (group_cipher == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); + } else if (group_cipher == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (pairwise_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (pairwise_cipher == WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + } else { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else { + wpa_printf(MSG_WARNING, "Invalid key management type (%d).", + key_mgmt); + return -1; + } + pos += WPA_SELECTOR_LEN; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - wpa_ie) - 2; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} + + +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm) +{ +#ifndef CONFIG_NO_WPA2 + u8 *pos; + struct rsn_ie_hdr *hdr; + u16 capab; + + if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { + wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", + (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (group_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (group_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (group_cipher == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); + } else if (group_cipher == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (pairwise_cipher == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (pairwise_cipher == WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + } else { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); +#ifdef CONFIG_IEEE80211R + } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_WARNING, "Invalid key management type (%d).", + key_mgmt); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (sm->cur_pmksa) { + /* PMKID Count (2 octets, little endian) */ + *pos++ = 1; + *pos++ = 0; + /* PMKID */ + os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + if (!sm->cur_pmksa) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - rsn_ie) - 2; + + WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); + + return pos - rsn_ie; +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + + +/** + * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE + * @wpa_ie_len: Maximum length of the generated WPA/RSN IE + * Returns: Length of the generated WPA/RSN IE or -1 on failure + */ +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) +{ + if (sm->proto == WPA_PROTO_RSN) + return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt, sm->mgmt_group_cipher, + sm); + else + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h new file mode 100644 index 0000000000000..17e375aa21d89 --- /dev/null +++ b/src/rsn_supp/wpa_ie.h @@ -0,0 +1,52 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_IE_H +#define WPA_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); + +#endif /* WPA_IE_H */ diff --git a/src/tls/.gitignore b/src/tls/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/tls/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/tls/Makefile b/src/tls/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/tls/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/tls/asn1.c b/src/tls/asn1.c new file mode 100644 index 0000000000000..96bc1ac78a9d9 --- /dev/null +++ b/src/tls/asn1.c @@ -0,0 +1,209 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" + +#ifdef CONFIG_INTERNAL_X509 + +#include "asn1.h" + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) +{ + const u8 *pos, *end; + u8 tmp; + + os_memset(hdr, 0, sizeof(*hdr)); + pos = buf; + end = buf + len; + + hdr->identifier = *pos++; + hdr->class = hdr->identifier >> 6; + hdr->constructed = !!(hdr->identifier & (1 << 5)); + + if ((hdr->identifier & 0x1f) == 0x1f) { + hdr->tag = 0; + do { + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: Identifier " + "underflow"); + return -1; + } + tmp = *pos++; + wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: " + "0x%02x", tmp); + hdr->tag = (hdr->tag << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + } else + hdr->tag = hdr->identifier & 0x1f; + + tmp = *pos++; + if (tmp & 0x80) { + if (tmp == 0xff) { + wpa_printf(MSG_DEBUG, "ASN.1: Reserved length " + "value 0xff used"); + return -1; + } + tmp &= 0x7f; /* number of subsequent octets */ + hdr->length = 0; + if (tmp > 4) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long length field"); + return -1; + } + while (tmp--) { + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: Length " + "underflow"); + return -1; + } + hdr->length = (hdr->length << 8) | *pos++; + } + } else { + /* Short form - length 0..127 in one octet */ + hdr->length = tmp; + } + + if (end < pos || hdr->length > (unsigned int) (end - pos)) { + wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow"); + return -1; + } + + hdr->payload = pos; + return 0; +} + + +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + unsigned long val; + u8 tmp; + + os_memset(oid, 0, sizeof(*oid)); + + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { + wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + *next = end; + + while (pos < end) { + val = 0; + + do { + if (pos >= end) + return -1; + tmp = *pos++; + val = (val << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + + if (oid->len >= ASN1_MAX_OID_LEN) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value"); + return -1; + } + if (oid->len == 0) { + /* + * The first octet encodes the first two object + * identifier components in (X*40) + Y formula. + * X = 0..2. + */ + oid->oid[0] = val / 40; + if (oid->oid[0] > 2) + oid->oid[0] = 2; + oid->oid[1] = val - oid->oid[0] * 40; + oid->len = 2; + } else + oid->oid[oid->len++] = val; + } + + return 0; +} + + +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) +{ + char *pos = buf; + size_t i; + int ret; + + if (len == 0) + return; + + buf[0] = '\0'; + + for (i = 0; i < oid->len; i++) { + ret = os_snprintf(pos, buf + len - pos, + "%s%lu", + i == 0 ? "" : ".", oid->oid[i]); + if (ret < 0 || ret >= buf + len - pos) + break; + pos += ret; + } + buf[len - 1] = '\0'; +} + + +static u8 rotate_bits(u8 octet) +{ + int i; + u8 res; + + res = 0; + for (i = 0; i < 8; i++) { + res <<= 1; + if (octet & 1) + res |= 1; + octet >>= 1; + } + + return res; +} + + +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) +{ + unsigned long val = 0; + const u8 *pos = buf; + + /* BER requires that unused bits are zero, so we can ignore the number + * of unused bits */ + pos++; + + if (len >= 2) + val |= rotate_bits(*pos++); + if (len >= 3) + val |= ((unsigned long) rotate_bits(*pos++)) << 8; + if (len >= 4) + val |= ((unsigned long) rotate_bits(*pos++)) << 16; + if (len >= 5) + val |= ((unsigned long) rotate_bits(*pos++)) << 24; + if (len >= 6) + wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored " + "(BIT STRING length %lu)", + __func__, (unsigned long) len); + + return val; +} + +#endif /* CONFIG_INTERNAL_X509 */ diff --git a/src/tls/asn1.h b/src/tls/asn1.h new file mode 100644 index 0000000000000..c02ada8278a2c --- /dev/null +++ b/src/tls/asn1.h @@ -0,0 +1,71 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef ASN1_H +#define ASN1_H + +#define ASN1_TAG_EOC 0x00 /* not used with DER */ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_BITSTRING 0x03 +#define ASN1_TAG_OCTETSTRING 0x04 +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_OID 0x06 +#define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */ +#define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ +#define ASN1_TAG_REAL 0x09 /* not yet parsed */ +#define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ +#define ANS1_TAG_RELATIVE_OID 0x0D +#define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ +#define ASN1_TAG_PRINTABLESTRING 0x13 +#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ +#define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ +#define ASN1_TAG_IA5STRING 0x16 +#define ASN1_TAG_UTCTIME 0x17 +#define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */ +#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */ +#define ASN1_TAG_VISIBLESTRING 0x1A +#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ +#define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ + +#define ASN1_CLASS_UNIVERSAL 0 +#define ASN1_CLASS_APPLICATION 1 +#define ASN1_CLASS_CONTEXT_SPECIFIC 2 +#define ASN1_CLASS_PRIVATE 3 + + +struct asn1_hdr { + const u8 *payload; + u8 identifier, class, constructed; + unsigned int tag, length; +}; + +#define ASN1_MAX_OID_LEN 20 +struct asn1_oid { + unsigned long oid[ASN1_MAX_OID_LEN]; + size_t len; +}; + + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next); +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); + +#endif /* ASN1_H */ diff --git a/src/tls/asn1_test.c b/src/tls/asn1_test.c new file mode 100644 index 0000000000000..a5c77535309f4 --- /dev/null +++ b/src/tls/asn1_test.c @@ -0,0 +1,210 @@ +/* + * Testing tool for ASN.1/X.509v3 routines + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "asn1.h" +#include "x509v3.h" + +extern int wpa_debug_level; + + +static const char * asn1_class_str(int class) +{ + switch (class) { + case ASN1_CLASS_UNIVERSAL: + return "Universal"; + case ASN1_CLASS_APPLICATION: + return "Application"; + case ASN1_CLASS_CONTEXT_SPECIFIC: + return "Context-specific"; + case ASN1_CLASS_PRIVATE: + return "Private"; + default: + return "?"; + } +} + + +int asn1_parse(const u8 *buf, size_t len, int level) +{ + const u8 *pos, *prev, *end; + char prefix[10], str[100]; + int _level; + struct asn1_hdr hdr; + struct asn1_oid oid; + u8 tmp; + + _level = level; + if ((size_t) _level > sizeof(prefix) - 1) + _level = sizeof(prefix) - 1; + memset(prefix, ' ', _level); + prefix[_level] = '\0'; + + pos = buf; + end = buf + len; + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + prev = pos; + pos = hdr.payload; + + wpa_printf(MSG_MSGDUMP, "ASN.1:%s Class %d(%s) P/C %d(%s) " + "Tag %u Length %u", + prefix, hdr.class, asn1_class_str(hdr.class), + hdr.constructed, + hdr.constructed ? "Constructed" : "Primitive", + hdr.tag, hdr.length); + + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && + hdr.constructed) { + if (asn1_parse(pos, hdr.length, level + 1) < 0) + return -1; + pos += hdr.length; + } + + if (hdr.class != ASN1_CLASS_UNIVERSAL) + continue; + + switch (hdr.tag) { + case ASN1_TAG_EOC: + if (hdr.length) { + wpa_printf(MSG_DEBUG, "ASN.1: Non-zero " + "end-of-contents length (%u)", + hdr.length); + return -1; + } + wpa_printf(MSG_MSGDUMP, "ASN.1:%s EOC", prefix); + break; + case ASN1_TAG_BOOLEAN: + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "ASN.1: Unexpected " + "Boolean length (%u)", hdr.length); + return -1; + } + tmp = *pos++; + wpa_printf(MSG_MSGDUMP, "ASN.1:%s Boolean %s", + prefix, tmp ? "TRUE" : "FALSE"); + break; + case ASN1_TAG_INTEGER: + wpa_hexdump(MSG_MSGDUMP, "ASN.1: INTEGER", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_BITSTRING: + wpa_hexdump(MSG_MSGDUMP, "ASN.1: BitString", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_OCTETSTRING: + wpa_hexdump(MSG_MSGDUMP, "ASN.1: OctetString", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_NULL: + if (hdr.length) { + wpa_printf(MSG_DEBUG, "ASN.1: Non-zero Null " + "length (%u)", hdr.length); + return -1; + } + wpa_printf(MSG_MSGDUMP, "ASN.1:%s Null", prefix); + break; + case ASN1_TAG_OID: + if (asn1_get_oid(prev, end - prev, &oid, &prev) < 0) { + wpa_printf(MSG_DEBUG, "ASN.1: Invalid OID"); + return -1; + } + asn1_oid_to_str(&oid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, "ASN.1:%s OID %s", prefix, str); + pos += hdr.length; + break; + case ANS1_TAG_RELATIVE_OID: + wpa_hexdump(MSG_MSGDUMP, "ASN.1: Relative OID", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_SEQUENCE: + wpa_printf(MSG_MSGDUMP, "ASN.1:%s SEQUENCE", prefix); + if (asn1_parse(pos, hdr.length, level + 1) < 0) + return -1; + pos += hdr.length; + break; + case ASN1_TAG_SET: + wpa_printf(MSG_MSGDUMP, "ASN.1:%s SET", prefix); + if (asn1_parse(pos, hdr.length, level + 1) < 0) + return -1; + pos += hdr.length; + break; + case ASN1_TAG_PRINTABLESTRING: + wpa_hexdump_ascii(MSG_MSGDUMP, + "ASN.1: PrintableString", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_IA5STRING: + wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: IA5String", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_UTCTIME: + wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: UTCTIME", + pos, hdr.length); + pos += hdr.length; + break; + case ASN1_TAG_VISIBLESTRING: + wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: VisibleString", + pos, hdr.length); + pos += hdr.length; + break; + default: + wpa_printf(MSG_DEBUG, "ASN.1: Unknown tag %d", + hdr.tag); + return -1; + } + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + FILE *f; + u8 buf[3000]; + size_t len; + struct x509_certificate *cert; + + wpa_debug_level = 0; + + f = fopen(argv[1], "rb"); + if (f == NULL) + return -1; + len = fread(buf, 1, sizeof(buf), f); + fclose(f); + + if (asn1_parse(buf, len, 0) < 0) + printf("Failed to parse DER ASN.1\n"); + + printf("\n\n"); + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) + printf("Failed to parse X.509 certificate\n"); + x509_certificate_free(cert); + + return 0; +} diff --git a/src/tls/bignum.c b/src/tls/bignum.c new file mode 100644 index 0000000000000..5c0fc62edeb59 --- /dev/null +++ b/src/tls/bignum.c @@ -0,0 +1,230 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "bignum.h" + +#ifdef CONFIG_INTERNAL_LIBTOMMATH +#include "libtommath.c" +#else /* CONFIG_INTERNAL_LIBTOMMATH */ +#include <tommath.h> +#endif /* CONFIG_INTERNAL_LIBTOMMATH */ + + +/* + * The current version is just a wrapper for LibTomMath library, so + * struct bignum is just typecast to mp_int. + */ + +/** + * bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct bignum * bignum_init(void) +{ + struct bignum *n = os_zalloc(sizeof(mp_int)); + if (n == NULL) + return NULL; + if (mp_init((mp_int *) n) != MP_OKAY) { + os_free(n); + n = NULL; + } + return n; +} + + +/** + * bignum_deinit - Free bignum + * @n: Bignum from bignum_init() + */ +void bignum_deinit(struct bignum *n) +{ + if (n) { + mp_clear((mp_int *) n); + os_free(n); + } +} + + +/** + * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer + * @n: Bignum from bignum_init() + * Returns: Length of n if written to a binary buffer + */ +size_t bignum_get_unsigned_bin_len(struct bignum *n) +{ + return mp_unsigned_bin_size((mp_int *) n); +} + + +/** + * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum + * @n: Bignum from bignum_init() + * @buf: Buffer for the binary number + * @len: Length of the buffer, can be %NULL if buffer is known to be long + * enough. Set to used buffer length on success if not %NULL. + * Returns: 0 on success, -1 on failure + */ +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len) +{ + size_t need = mp_unsigned_bin_size((mp_int *) n); + if (len && need > *len) { + *len = need; + return -1; + } + if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + if (len) + *len = need; + return 0; +} + + +/** + * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer + * @n: Bignum from bignum_init(); to be set to the given value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: 0 on success, -1 on failure + */ +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len) +{ + if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_cmp - Signed comparison + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * Returns: 0 on success, -1 on failure + */ +int bignum_cmp(const struct bignum *a, const struct bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +/** + * bignum_cmd_d - Compare bignum to standard integer + * @a: Bignum from bignum_init() + * @b: Small integer + * Returns: 0 on success, -1 on failure + */ +int bignum_cmp_d(const struct bignum *a, unsigned long b) +{ + return mp_cmp_d((mp_int *) a, b); +} + + +/** + * bignum_add - c = a + b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_sub - c = a - b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mul - c = a * b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a * b + * Returns: 0 on success, -1 on failure + */ +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mulmod - d = a * b (mod c) + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a * b (mod c) + * Returns: 0 on success, -1 on failure + */ +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum from bignum_init(); base + * @b: Bignum from bignum_init(); exponent + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} diff --git a/src/tls/bignum.h b/src/tls/bignum.h new file mode 100644 index 0000000000000..f25e26783a0ab --- /dev/null +++ b/src/tls/bignum.h @@ -0,0 +1,38 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +struct bignum; + +struct bignum * bignum_init(void); +void bignum_deinit(struct bignum *n); +size_t bignum_get_unsigned_bin_len(struct bignum *n); +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); +int bignum_cmp(const struct bignum *a, const struct bignum *b); +int bignum_cmp_d(const struct bignum *a, unsigned long b); +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); + +#endif /* BIGNUM_H */ diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c new file mode 100644 index 0000000000000..137426421c299 --- /dev/null +++ b/src/tls/libtommath.c @@ -0,0 +1,3381 @@ +/* + * Minimal code for RSA support from LibTomMath 0.41 + * http://libtom.org/ + * http://libtom.org/files/ltm-0.41.tar.bz2 + * This library was released in public domain by Tom St Denis. + * + * The combination in this file may not use all of the optimized algorithms + * from LibTomMath and may be considerable slower than the LibTomMath with its + * default settings. The main purpose of having this version here is to make it + * easier to build bignum.c wrapper without having to install and build an + * external library. + * + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this + * libtommath.c file instead of using the external LibTomMath library. + */ + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +#else /* LTM_FAST */ + +#define BN_MP_DIV_SMALL +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) + +typedef unsigned long mp_digit; +typedef u64 mp_word; + +#define DIGIT_BIT 28 +#define MP_28BIT + + +#define XMALLOC os_malloc +#define XFREE os_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + +/* prototypes for copied functions */ +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +static int s_mp_sqr(mp_int * a, mp_int * b); +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); + +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...); +#endif +static int mp_lshd(mp_int * a, int b); +static void mp_set(mp_int * a, mp_digit b); +static void mp_clamp(mp_int * a); +static void mp_exch(mp_int * a, mp_int * b); +static void mp_rshd(mp_int * a, int b); +static void mp_zero(mp_int * a); +static int mp_mod_2d(mp_int * a, int b, mp_int * c); +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); +static int mp_init_copy(mp_int * a, mp_int * b); +static int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP +static int mp_div_2(mp_int * a, mp_int * b); +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ +static int mp_copy(mp_int * a, mp_int * b); +static int mp_count_bits(mp_int * a); +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); +static int mp_mod(mp_int * a, mp_int * b, mp_int * c); +static int mp_grow(mp_int * a, int size); +static int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C +static int mp_abs(mp_int * a, mp_int * b); +#endif +static int mp_sqr(mp_int * a, mp_int * b); +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); +static int mp_2expt(mp_int * a, int b); +static int mp_reduce_setup(mp_int * a, mp_int * b); +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); +static int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C +static int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ + + + +/* functions from bn_<func name>.c */ + + +/* reverse an array, used for radix code */ +static void bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} + + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* init a new mp_int */ +static int mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + + +/* clear one (frees) */ +static void mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + + +/* high level addition (handles signs) */ +static int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* high level subtraction (handles signs) */ +static int mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* high level multiplication (handles sign) */ +static int mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ +#ifdef BN_FAST_S_MP_MUL_DIGS_C + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +#error mp_mul could fail + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} + + +/* d = a * b (mod c) */ +static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} + + +/* c = a mod b, 0 <= c < b */ +static int mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef LTM_NO_NEG_EXP + return MP_VAL; +#else /* LTM_NO_NEG_EXP */ +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else +#error mp_exptmod would always fail + /* no invmod */ + return MP_VAL; +#endif +#endif /* LTM_NO_NEG_EXP */ + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else +#error mp_exptmod could fail + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + + +/* compare two ints (signed)*/ +static int mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + + +/* compare a digit */ +static int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + +#ifndef BN_FAST_MP_INVMOD_C +#ifndef BN_MP_INVMOD_SLOW_C +#error mp_invmod would always fail +#endif +#endif + return MP_VAL; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* get the size for an unsigned equivalent */ +static int mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* compare maginitude of two ints (unsigned) */ +static int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + + +/* store in unsigned [big endian] format */ +static int mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} + + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + + +static int mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} + + +/* set to zero */ +static void mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} + + +/* copy, b = a */ +static int mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + + +/* shift right a certain amount of digits */ +static void mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +static void mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +static void mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + + +/* grow as required */ +static int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + + +#ifdef BN_MP_ABS_C +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +static int mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + + +/* set to a digit */ +static void mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} + + +#ifndef LTM_NO_NEG_EXP +/* b = a/2 */ +static int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* shift left by a certain bit count */ +static int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + + +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + + +/* shift left a certain amount of digits */ +static int mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + + +/* returns the number of bits in an int */ +static int mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + + +/* calc a value mod 2**b */ +static int mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + + +/* computes b = a*a */ +static int mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else +#error mp_sqr could fail + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} + + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* determines the setup value */ +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} + + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +static int mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} + + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +static int mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} + + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { +#error mp_reduce would always fail + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + if ((res = mp_add (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +/* init an mp_init for a given size */ +static int mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} + + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +static int s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* setups the montgomery reduction stuff */ +static int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +static int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_EXPTMOD_FAST_C +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +#ifdef BN_FAST_S_MP_SQR_C +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +static int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_D_C +/* multiply by a digit */ +static int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/src/tls/rsa.c b/src/tls/rsa.c new file mode 100644 index 0000000000000..bfc0d5222495d --- /dev/null +++ b/src/tls/rsa.c @@ -0,0 +1,359 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "asn1.h" +#include "bignum.h" +#include "rsa.h" + + +struct crypto_rsa_key { + int private_key; /* whether private key is set */ + struct bignum *n; /* modulus (p * q) */ + struct bignum *e; /* public exponent */ + /* The following parameters are available only if private_key is set */ + struct bignum *d; /* private exponent */ + struct bignum *p; /* prime p (factor of n) */ + struct bignum *q; /* prime q (factor of n) */ + struct bignum *dmp1; /* d mod (p - 1); CRT exponent */ + struct bignum *dmq1; /* d mod (q - 1); CRT exponent */ + struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */ +}; + + +static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, + struct bignum *num) +{ + struct asn1_hdr hdr; + + if (pos == NULL) + return NULL; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return NULL; + } + + if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) { + wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER"); + return NULL; + } + + return hdr.payload + hdr.length; +} + + +/** + * crypto_rsa_import_public_key - Import an RSA public key + * @buf: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->n = bignum_init(); + key->e = bignum_init(); + if (key->n == NULL || key->e == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.1: + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_import_private_key - Import an RSA private key + * @buf: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * Returns: Pointer to the private key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct bignum *zero; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->private_key = 1; + + key->n = bignum_init(); + key->e = bignum_init(); + key->d = bignum_init(); + key->p = bignum_init(); + key->q = bignum_init(); + key->dmp1 = bignum_init(); + key->dmq1 = bignum_init(); + key->iqmp = bignum_init(); + + if (key->n == NULL || key->e == NULL || key->d == NULL || + key->p == NULL || key->q == NULL || key->dmp1 == NULL || + key->dmq1 == NULL || key->iqmp == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.2: + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER -- (inverse of q) mod p + * } + * + * Version ::= INTEGER -- shall be 0 for this version of the standard + */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + zero = bignum_init(); + if (zero == NULL) + goto error; + pos = crypto_rsa_parse_integer(pos, end, zero); + if (pos == NULL || bignum_cmp_d(zero, 0) != 0) { + wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the " + "beginning of private key; not found"); + bignum_deinit(zero); + goto error; + } + bignum_deinit(zero); + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + pos = crypto_rsa_parse_integer(pos, end, key->d); + pos = crypto_rsa_parse_integer(pos, end, key->p); + pos = crypto_rsa_parse_integer(pos, end, key->q); + pos = crypto_rsa_parse_integer(pos, end, key->dmp1); + pos = crypto_rsa_parse_integer(pos, end, key->dmq1); + pos = crypto_rsa_parse_integer(pos, end, key->iqmp); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key + * @key: RSA key + * Returns: Modulus length of the key + */ +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key) +{ + return bignum_get_unsigned_bin_len(key->n); +} + + +/** + * crypto_rsa_exptmod - RSA modular exponentiation + * @in: Input data + * @inlen: Input data length + * @out: Buffer for output data + * @outlen: Maximum size of the output buffer and used size on success + * @key: RSA key + * @use_private: 1 = Use RSA private key, 0 = Use RSA public key + * Returns: 0 on success, -1 on failure + */ +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private) +{ + struct bignum *tmp, *a = NULL, *b = NULL; + int ret = -1; + size_t modlen; + + if (use_private && !key->private_key) + return -1; + + tmp = bignum_init(); + if (tmp == NULL) + return -1; + + if (bignum_set_unsigned_bin(tmp, in, inlen) < 0) + goto error; + if (bignum_cmp(key->n, tmp) < 0) { + /* Too large input value for the RSA key modulus */ + goto error; + } + + if (use_private) { + /* + * Decrypt (or sign) using Chinese remainer theorem to speed + * up calculation. This is equivalent to tmp = tmp^d mod n + * (which would require more CPU to calculate directly). + * + * dmp1 = (1/e) mod (p-1) + * dmq1 = (1/e) mod (q-1) + * iqmp = (1/q) mod p, where p > q + * m1 = c^dmp1 mod p + * m2 = c^dmq1 mod q + * h = q^-1 (m1 - m2) mod p + * m = m2 + hq + */ + a = bignum_init(); + b = bignum_init(); + if (a == NULL || b == NULL) + goto error; + + /* a = tmp^dmp1 mod p */ + if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0) + goto error; + + /* b = tmp^dmq1 mod q */ + if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0) + goto error; + + /* tmp = (a - b) * (1/q mod p) (mod p) */ + if (bignum_sub(a, b, tmp) < 0 || + bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0) + goto error; + + /* tmp = b + q * tmp */ + if (bignum_mul(tmp, key->q, tmp) < 0 || + bignum_add(tmp, b, tmp) < 0) + goto error; + } else { + /* Encrypt (or verify signature) */ + /* tmp = tmp^e mod N */ + if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0) + goto error; + } + + modlen = crypto_rsa_get_modulus_len(key); + if (modlen > *outlen) { + *outlen = modlen; + goto error; + } + + if (bignum_get_unsigned_bin_len(tmp) > modlen) + goto error; /* should never happen */ + + *outlen = modlen; + os_memset(out, 0, modlen); + if (bignum_get_unsigned_bin( + tmp, out + + (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0) + goto error; + + ret = 0; + +error: + bignum_deinit(tmp); + bignum_deinit(a); + bignum_deinit(b); + return ret; +} + + +/** + * crypto_rsa_free - Free RSA key + * @key: RSA key to be freed + * + * This function frees an RSA key imported with either + * crypto_rsa_import_public_key() or crypto_rsa_import_private_key(). + */ +void crypto_rsa_free(struct crypto_rsa_key *key) +{ + if (key) { + bignum_deinit(key->n); + bignum_deinit(key->e); + bignum_deinit(key->d); + bignum_deinit(key->p); + bignum_deinit(key->q); + bignum_deinit(key->dmp1); + bignum_deinit(key->dmq1); + bignum_deinit(key->iqmp); + os_free(key); + } +} diff --git a/src/tls/rsa.h b/src/tls/rsa.h new file mode 100644 index 0000000000000..ac50dfd69d3e6 --- /dev/null +++ b/src/tls/rsa.h @@ -0,0 +1,29 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef RSA_H +#define RSA_H + +struct crypto_rsa_key; + +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len); +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len); +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private); +void crypto_rsa_free(struct crypto_rsa_key *key); + +#endif /* RSA_H */ diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c new file mode 100644 index 0000000000000..302e3ee13946c --- /dev/null +++ b/src/tls/tlsv1_client.c @@ -0,0 +1,660 @@ +/* + * TLSv1 client (RFC 2246) + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +void tlsv1_client_free_dh(struct tlsv1_client *conn) +{ + os_free(conn->dh_p); + os_free(conn->dh_g); + os_free(conn->dh_ys); + conn->dh_p = conn->dh_g = conn->dh_ys = NULL; +} + + +int tls_derive_pre_master_secret(u8 *pre_master_secret) +{ + WPA_PUT_BE16(pre_master_secret, TLS_VERSION); + if (os_get_random(pre_master_secret + 2, + TLS_PRE_MASTER_SECRET_LEN - 2)) + return -1; + return 0; +} + + +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(pre_master_secret, pre_master_secret_len, + "master secret", seed, 2 * TLS_RANDOM_LEN, + conn->master_secret, TLS_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "key expansion", seed, 2 * TLS_RANDOM_LEN, + key_block, key_block_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + + return 0; +} + + +/** + * tlsv1_client_handshake - Process TLS handshake + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * @appl_data_len: Pointer to variable that is set to appl_data length + * Returns: Pointer to output data, %NULL on failure + */ +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len) +{ + const u8 *pos, *end; + u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + size_t in_msg_len; + int no_appl_data; + + if (conn->state == CLIENT_HELLO) { + if (in_len) + return NULL; + return tls_send_client_hello(conn, out_len); + } + + if (in_data == NULL || in_len == 0) + return NULL; + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + if (tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert)) { + wpa_printf(MSG_DEBUG, "TLSv1: Processing received " + "record failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } + ct = pos[0]; + + in_pos = in_msg; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_client_process_handshake(conn, ct, in_pos, + &in_msg_len, + appl_data, + appl_data_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + } + + os_free(in_msg); + in_msg = NULL; + + no_appl_data = appl_data == NULL || *appl_data == NULL; + msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); + +failed: + os_free(in_msg); + if (conn->alert_level) { + conn->state = FAILED; + os_free(msg); + msg = tlsv1_client_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } else if (msg == NULL) { + msg = os_zalloc(1); + *out_len = 0; + } + + return msg; +} + + +/** + * tlsv1_client_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + out_data, out_len, in_len, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_client_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +int tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + const u8 *in_end, *pos; + int res; + u8 alert, *out_end, *out_pos; + size_t olen; + + pos = in_data; + in_end = in_data + in_len; + out_pos = out_data; + out_end = out_data + out_len; + + while (pos < in_end) { + if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x", pos[0]); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + olen = out_end - out_pos; + res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + out_pos += olen; + if (out_pos > out_end) { + wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " + "for processing the received record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + } + + return out_pos - out_data; +} + + +/** + * tlsv1_client_global_init - Initialize TLSv1 client + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 client functions. + */ +int tlsv1_client_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_client_global_deinit - Deinitialize TLSv1 client + * + * This function can be used to deinitialize the TLSv1 client that was + * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions + * can be called after this before calling tlsv1_client_global_init() again. + */ +void tlsv1_client_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_client_init - Initialize TLSv1 client connection + * Returns: Pointer to TLSv1 client connection data or %NULL on failure + */ +struct tlsv1_client * tlsv1_client_init(void) +{ + struct tlsv1_client *conn; + size_t count; + u16 *suites; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; +#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; +#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + conn->num_cipher_suites = count; + + return conn; +} + + +/** + * tlsv1_client_deinit - Deinitialize TLSv1 client connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + */ +void tlsv1_client_deinit(struct tlsv1_client *conn) +{ + crypto_public_key_free(conn->server_rsa_key); + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + os_free(conn->client_hello_ext); + tlsv1_client_free_dh(conn); + tlsv1_cred_free(conn->cred); + os_free(conn); +} + + +/** + * tlsv1_client_established - Check whether connection has been established + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_client_established(struct tlsv1_client *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_client_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_client_get_cipher - Get current cipher name + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + cipher = "ADH-AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + cipher = "AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + default: + return -1; + } + + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; + return 0; +} + + +/** + * tlsv1_client_shutdown - Shutdown TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_shutdown(struct tlsv1_client *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + + conn->certificate_requested = 0; + crypto_public_key_free(conn->server_rsa_key); + conn->server_rsa_key = NULL; + conn->session_resumed = 0; + + return 0; +} + + +/** + * tlsv1_client_resumed - Was session resumption used + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_client_resumed(struct tlsv1_client *conn) +{ + return !!conn->session_resumed; +} + + +/** + * tlsv1_client_hello_ext - Set TLS extension for ClientHello + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ext_type: Extension type + * @data: Extension payload (%NULL to remove extension) + * @data_len: Extension payload length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len) +{ + u8 *pos; + + conn->session_ticket_included = 0; + os_free(conn->client_hello_ext); + conn->client_hello_ext = NULL; + conn->client_hello_ext_len = 0; + + if (data == NULL || data_len == 0) + return 0; + + pos = conn->client_hello_ext = os_malloc(6 + data_len); + if (pos == NULL) + return -1; + + WPA_PUT_BE16(pos, 4 + data_len); + pos += 2; + WPA_PUT_BE16(pos, ext_type); + pos += 2; + WPA_PUT_BE16(pos, data_len); + pos += 2; + os_memcpy(pos, data, data_len); + conn->client_hello_ext_len = 6 + data_len; + + if (ext_type == TLS_EXT_PAC_OPAQUE) { + conn->session_ticket_included = 1; + wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket"); + } + + return 0; +} + + +/** + * tlsv1_client_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_client_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_client_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) +{ +#ifdef EAP_FAST + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; +#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; +#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +#else /* EAP_FAST */ + return -1; +#endif /* EAP_FAST */ +} + + +/** + * tlsv1_client_set_cred - Set client credentials + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @cred: Credentials from tlsv1_cred_alloc() + * Returns: 0 on success, -1 on failure + * + * On success, the client takes ownership of the credentials block and caller + * must not free it. On failure, caller is responsible for freeing the + * credential block. + */ +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred) +{ + tlsv1_cred_free(conn->cred); + conn->cred = cred; + return 0; +} + + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h new file mode 100644 index 0000000000000..16ad57d4007fe --- /dev/null +++ b/src/tls/tlsv1_client.h @@ -0,0 +1,59 @@ +/* + * TLSv1 client (RFC 2246) + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_CLIENT_H +#define TLSV1_CLIENT_H + +#include "tlsv1_cred.h" + +struct tlsv1_client; + +int tlsv1_client_global_init(void); +void tlsv1_client_global_deinit(void); +struct tlsv1_client * tlsv1_client_init(void); +void tlsv1_client_deinit(struct tlsv1_client *conn); +int tlsv1_client_established(struct tlsv1_client *conn); +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len); +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen); +int tlsv1_client_shutdown(struct tlsv1_client *conn); +int tlsv1_client_resumed(struct tlsv1_client *conn); +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len); +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred); + +typedef int (*tlsv1_client_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_CLIENT_H */ diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h new file mode 100644 index 0000000000000..7fe179f10c63b --- /dev/null +++ b/src/tls/tlsv1_client_i.h @@ -0,0 +1,87 @@ +/* + * TLSv1 client - internal structures + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_CLIENT_I_H +#define TLSV1_CLIENT_I_H + +struct tlsv1_client { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + unsigned int certificate_requested:1; + unsigned int session_resumed:1; + unsigned int session_ticket_included:1; + unsigned int use_session_ticket:1; + + struct crypto_public_key *server_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 prev_cipher_suite; + + u8 *client_hello_ext; + size_t client_hello_ext_len; + + /* The prime modulus used for Diffie-Hellman */ + u8 *dh_p; + size_t dh_p_len; + /* The generator used for Diffie-Hellman */ + u8 *dh_g; + size_t dh_g_len; + /* The server's Diffie-Hellman public value */ + u8 *dh_ys; + size_t dh_ys_len; + + struct tlsv1_credentials *cred; + + tlsv1_client_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; +}; + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); +void tlsv1_client_free_dh(struct tlsv1_client *conn); +int tls_derive_pre_master_secret(u8 *pre_master_secret); +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len); +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len); +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data); +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len); + +#endif /* TLSV1_CLIENT_I_H */ diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c new file mode 100644 index 0000000000000..ee20330ce2c6b --- /dev/null +++ b/src/tls/tlsv1_client_read.c @@ -0,0 +1,976 @@ +/* + * TLSv1 client - read handshake message + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "x509v3.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); + + +static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, i; + u16 cipher_suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHello)", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ServerHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len); + end = pos + len; + + /* ProtocolVersion server_version */ + if (end - pos < 2) + goto decode_error; + if (WPA_GET_BE16(pos) != TLS_VERSION) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "ServerHello"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_PROTOCOL_VERSION); + return -1; + } + pos += 2; + + /* Random random */ + if (end - pos < TLS_RANDOM_LEN) + goto decode_error; + + os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + if (conn->session_id_len && conn->session_id_len == *pos && + os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) { + pos += 1 + conn->session_id_len; + wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session"); + conn->session_resumed = 1; + } else { + conn->session_id_len = *pos; + pos++; + os_memcpy(conn->session_id, pos, conn->session_id_len); + pos += conn->session_id_len; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* CipherSuite cipher_suite */ + if (end - pos < 2) + goto decode_error; + cipher_suite = WPA_GET_BE16(pos); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + if (cipher_suite == conn->cipher_suites[i]) + break; + } + if (i == conn->num_cipher_suites) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "cipher suite 0x%04x", cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) { + wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different " + "cipher suite for a resumed connection (0x%04x != " + "0x%04x)", cipher_suite, conn->prev_cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->prev_cipher_suite = cipher_suite; + + /* CompressionMethod compression_method */ + if (end - pos < 1) + goto decode_error; + if (*pos != TLS_COMPRESSION_NULL) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "compression 0x%02x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + pos++; + + if (end != pos) { + /* TODO: ServerHello extensions */ + wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " + "end of ServerHello", pos, end - pos); + goto decode_error; + } + + if (conn->session_ticket_included && conn->session_ticket_cb) { + /* TODO: include SessionTicket extension if one was included in + * ServerHello */ + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = !!res; + } + + if ((conn->session_resumed || conn->use_session_ticket) && + tls_derive_keys(conn, NULL, 0)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) + return tls_process_server_key_exchange(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ServerKeyExchange/CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->server_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->server_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (conn->cred && + x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason) < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + +static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + + tlsv1_client_free_dh(conn); + + pos = buf; + end = buf + len; + + if (end - pos < 3) + goto fail; + conn->dh_p_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", + (unsigned long) conn->dh_p_len); + goto fail; + } + conn->dh_p = os_malloc(conn->dh_p_len); + if (conn->dh_p == NULL) + goto fail; + os_memcpy(conn->dh_p, pos, conn->dh_p_len); + pos += conn->dh_p_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", + conn->dh_p, conn->dh_p_len); + + if (end - pos < 3) + goto fail; + conn->dh_g_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + goto fail; + conn->dh_g = os_malloc(conn->dh_g_len); + if (conn->dh_g == NULL) + goto fail; + os_memcpy(conn->dh_g, pos, conn->dh_g_len); + pos += conn->dh_g_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", + conn->dh_g, conn->dh_g_len); + if (conn->dh_g_len == 1 && conn->dh_g[0] < 2) + goto fail; + + if (end - pos < 3) + goto fail; + conn->dh_ys_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + goto fail; + conn->dh_ys = os_malloc(conn->dh_ys_len); + if (conn->dh_ys == NULL) + goto fail; + os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); + pos += conn->dh_ys_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + conn->dh_ys, conn->dh_ys_len); + + return 0; + +fail: + wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed"); + tlsv1_client_free_dh(conn); + return -1; +} + + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange " + "(Left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerKeyExchange/" + "CertificateRequest/ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange"); + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed " + "with the selected cipher suite"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + } else { + wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + *in_len = end - in_data; + + conn->state = SERVER_CERTIFICATE_REQUEST; + + return 0; +} + + +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest"); + + conn->certificate_requested = 1; + + *in_len = end - in_data; + + conn->state = SERVER_HELLO_DONE; + + return 0; +} + + +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + + *in_len = end - in_data; + + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + if (conn->use_session_ticket) { + int res; + wpa_printf(MSG_DEBUG, "TLSv1: Server may have " + "rejected SessionTicket"); + conn->use_session_ticket = 0; + + /* Notify upper layers that SessionTicket failed */ + res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, NULL, + NULL, NULL); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket " + "callback indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + + conn->state = SERVER_CERTIFICATE; + return tls_process_certificate(conn, ct, in_data, + in_len); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + + conn->state = SERVER_FINISHED; + + return 0; +} + + +static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_server == NULL || + crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + CHANGE_CIPHER_SPEC : ACK_FINISHED; + + return 0; +} + + +static int tls_process_application_data(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len, + u8 **out_data, size_t *out_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake", + pos, left); + + *out_data = os_malloc(left); + if (*out_data) { + os_memcpy(*out_data, pos, left); + *out_len = left; + } + + return 0; +} + + +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 && + buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) { + size_t hr_len = WPA_GET_BE24(buf + 1); + if (hr_len > *len - 4) { + wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest"); + *len = 4 + hr_len; + return 0; + } + + switch (conn->state) { + case SERVER_HELLO: + if (tls_process_server_hello(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case SERVER_KEY_EXCHANGE: + if (tls_process_server_key_exchange(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE_REQUEST: + if (tls_process_certificate_request(conn, ct, buf, len)) + return -1; + break; + case SERVER_HELLO_DONE: + if (tls_process_server_hello_done(conn, ct, buf, len)) + return -1; + break; + case SERVER_CHANGE_CIPHER_SPEC: + if (tls_process_server_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case SERVER_FINISHED: + if (tls_process_server_finished(conn, ct, buf, len)) + return -1; + break; + case ACK_FINISHED: + if (out_data && + tls_process_application_data(conn, ct, buf, len, out_data, + out_len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) + tls_verify_hash_add(&conn->verify, buf, *len); + + return 0; +} diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c new file mode 100644 index 0000000000000..e0c95cbe8ebec --- /dev/null +++ b/src/tls/tlsv1_client_write.c @@ -0,0 +1,802 @@ +/* + * TLSv1 client - write handshake message + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "x509v3.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + if (conn->cred == NULL) + return 0; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) +{ + u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; + struct os_time now; + size_t len, i; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + *out_len = 0; + + os_get_time(&now); + WPA_PUT_BE32(conn->client_random, now.sec); + if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "client_random"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + hello = os_malloc(len); + if (hello == NULL) + return NULL; + end = hello + len; + + rhdr = hello; + pos = rhdr + TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientHello */ + /* ProtocolVersion client_version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suites<2..2^16-1> */ + WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + WPA_PUT_BE16(pos, conn->cipher_suites[i]); + pos += 2; + } + /* CompressionMethod compression_methods<1..2^8-1> */ + *pos++ = 1; + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->client_hello_ext) { + os_memcpy(pos, conn->client_hello_ext, + conn->client_hello_ext_len); + pos += conn->client_hello_ext_len; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, out_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(hello); + return NULL; + } + + conn->state = SERVER_HELLO; + + return hello; +} + + +static int tls_write_client_certificate(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred ? conn->cred->cert : NULL; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) { + /* + * Client was not configured with all the needed certificates + * to form a full certificate chain. The server may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ +#ifdef EAP_FAST + /* ClientDiffieHellmanPublic */ + u8 *csecret, *csecret_start, *dh_yc, *shared; + size_t csecret_len, dh_yc_len, shared_len; + + csecret_len = conn->dh_p_len; + csecret = os_malloc(csecret_len); + if (csecret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Yc (Diffie-Hellman)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (os_get_random(csecret, csecret_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data for Diffie-Hellman"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0) + csecret[0] = 0; /* make sure Yc < p */ + + csecret_start = csecret; + while (csecret_len > 1 && *csecret_start == 0) { + csecret_start++; + csecret_len--; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value", + csecret_start, csecret_len); + + /* Yc = g^csecret mod p */ + dh_yc_len = conn->dh_p_len; + dh_yc = os_malloc(dh_yc_len); + if (dh_yc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Diffie-Hellman"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + if (crypto_mod_exp(conn->dh_g, conn->dh_g_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + dh_yc, &dh_yc_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + WPA_PUT_BE16(*pos, dh_yc_len); + *pos += 2; + if (*pos + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " + "message buffer for Yc"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + os_memcpy(*pos, dh_yc, dh_yc_len); + *pos += dh_yc_len; + os_free(dh_yc); + + shared_len = conn->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + /* shared = Ys^csecret mod p */ + if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + shared, &shared_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(shared); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(csecret_start, 0, csecret_len); + os_free(csecret); + if (tls_derive_keys(conn, shared, shared_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(shared); + return -1; + } + os_memset(shared, 0, shared_len); + os_free(shared); + tlsv1_client_free_dh(conn); + return 0; +#else /* EAP_FAST */ + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); + return -1; +#endif /* EAP_FAST */ +} + + +static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ + u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN]; + size_t clen; + int res; + + if (tls_derive_pre_master_secret(pre_master_secret) < 0 || + tls_derive_keys(conn, pre_master_secret, + TLS_PRE_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* EncryptedPreMasterSecret */ + if (conn->server_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to " + "use for encrypting pre-master secret"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */ + *pos += 2; + clen = end - *pos; + res = crypto_public_key_encrypt_pkcs1_v15( + conn->server_rsa_key, + pre_master_secret, TLS_PRE_MASTER_SECRET_LEN, + *pos, &clen); + os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(*pos - 2, clen); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret", + *pos, clen); + *pos += clen; + + return 0; +} + + +static int tls_write_client_key_exchange(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange"); + + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientKeyExchange */ + if (keyx == TLS_KEY_X_DH_anon) { + if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + return -1; + } else { + if (tlsv1_key_x_rsa(conn, &pos, end) < 0) + return -1; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_certificate_verify(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; + size_t rlen, hlen, clen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* + * RFC 2246: 7.4.3 and 7.4.8: + * Signature signature + * + * RSA: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * DSA: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * + * The hash values are calculated over all handshake messages sent or + * received starting at ClientHello up to, but not including, this + * CertificateVerify message, including the type and length fields of + * the handshake messages. + */ + + hpos = hash; + + if (alg == SIGN_ALG_RSA) { + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); + + /* + * RFC 2246, 4.7: + * In digital signing, one-way hash functions are used as input for a + * signing algorithm. A digitally-signed element is encoded as an + * opaque vector <0..2^16-1>, where the length is specified by the + * signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA and one + * MD5) is signed (encrypted with the private key). It is encoded with + * PKCS #1 block type 0 or type 1 as described in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); + + pos += clen; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + *pos = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + rhdr, end - rhdr, 1, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos = rhdr + rlen; + + return 0; +} + + +static int tls_write_client_finished(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + + /* Encrypted Handshake Message: Finished */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_client == NULL || + crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", + verify_data, TLS_VERIFY_DATA_LEN); + + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); + pos += TLS_VERIFY_DATA_LEN; + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos = rhdr + rlen; + + *msgpos = pos; + + return 0; +} + + +static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 1000; + if (conn->certificate_requested) + msglen += tls_client_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (conn->certificate_requested) { + if (tls_write_client_certificate(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + } + + if (tls_write_client_key_exchange(conn, &pos, end) < 0 || + (conn->certificate_requested && conn->cred && conn->cred->key && + tls_write_client_certificate_verify(conn, &pos, end) < 0) || + tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = SERVER_CHANGE_CIPHER_SPEC; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " + "successfully"); + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data) +{ + switch (conn->state) { + case CLIENT_KEY_EXCHANGE: + return tls_send_client_key_exchange(conn, out_len); + case CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + case ACK_FINISHED: + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed " + "successfully"); + conn->state = ESTABLISHED; + *out_len = 0; + if (no_appl_data) { + /* Need to return something to get final TLS ACK. */ + return os_malloc(1); + } + return NULL; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c new file mode 100644 index 0000000000000..2f9dd0fa887d1 --- /dev/null +++ b/src/tls/tlsv1_common.c @@ -0,0 +1,241 @@ +/* + * TLSv1 common routines + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "x509v3.h" +#include "tlsv1_common.h" + + +/* + * TODO: + * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA + * Add support for commonly used cipher suites; don't bother with exportable + * suites. + */ + +static const struct tls_cipher_suite tls_cipher_suites[] = { + { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, + TLS_HASH_NULL }, + { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_MD5 }, + { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_SHA }, + { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA }, + { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, + TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, + { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA } +}; + +#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) +#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) + + +static const struct tls_cipher_data tls_ciphers[] = { + { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC2 }, + { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, + CRYPTO_CIPHER_ALG_3DES }, + { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16, + CRYPTO_CIPHER_ALG_AES }, + { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16, + CRYPTO_CIPHER_ALG_AES } +}; + +#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) + + +/** + * tls_get_cipher_suite - Get TLS cipher suite + * @suite: Cipher suite identifier + * Returns: Pointer to the cipher data or %NULL if not found + */ +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++) + if (tls_cipher_suites[i].suite == suite) + return &tls_cipher_suites[i]; + return NULL; +} + + +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_DATA; i++) + if (tls_ciphers[i].cipher == cipher) + return &tls_ciphers[i]; + return NULL; +} + + +int tls_server_key_exchange_allowed(tls_cipher cipher) +{ + const struct tls_cipher_suite *suite; + + /* RFC 2246, Section 7.4.3 */ + suite = tls_get_cipher_suite(cipher); + if (suite == NULL) + return 0; + + switch (suite->key_exchange) { + case TLS_KEY_X_DHE_DSS: + case TLS_KEY_X_DHE_DSS_EXPORT: + case TLS_KEY_X_DHE_RSA: + case TLS_KEY_X_DHE_RSA_EXPORT: + case TLS_KEY_X_DH_anon_EXPORT: + case TLS_KEY_X_DH_anon: + return 1; + case TLS_KEY_X_RSA_EXPORT: + return 1 /* FIX: public key len > 512 bits */; + default: + return 0; + } +} + + +/** + * tls_parse_cert - Parse DER encoded X.509 certificate and get public key + * @buf: ASN.1 DER encoded certificate + * @len: Length of the buffer + * @pk: Buffer for returning the allocated public key + * Returns: 0 on success, -1 on failure + * + * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves + * the public key from it. The caller is responsible for freeing the public key + * by calling crypto_public_key_free(). + */ +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk) +{ + struct x509_certificate *cert; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate", + buf, len); + + *pk = crypto_public_key_from_cert(buf, len); + if (*pk) + return 0; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 " + "certificate"); + return -1; + } + + /* TODO + * verify key usage (must allow encryption) + * + * All certificate profiles, key and cryptographic formats are + * defined by the IETF PKIX working group [PKIX]. When a key + * usage extension is present, the digitalSignature bit must be + * set for the key to be eligible for signing, as described + * above, and the keyEncipherment bit must be present to allow + * encryption, as described above. The keyAgreement bit must be + * set on Diffie-Hellman certificates. (PKIX: RFC 3280) + */ + + *pk = crypto_public_key_import(cert->public_key, cert->public_key_len); + x509_certificate_free(cert); + + if (*pk == NULL) { + wpa_printf(MSG_ERROR, "TLSv1: Failed to import " + "server public key"); + return -1; + } + + return 0; +} + + +int tls_verify_hash_init(struct tls_verify_hash *verify) +{ + tls_verify_hash_free(verify); + verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (verify->md5_client == NULL || verify->md5_server == NULL || + verify->md5_cert == NULL || verify->sha1_client == NULL || + verify->sha1_server == NULL || verify->sha1_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } + return 0; +} + + +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len) +{ + if (verify->md5_client && verify->sha1_client) { + crypto_hash_update(verify->md5_client, buf, len); + crypto_hash_update(verify->sha1_client, buf, len); + } + if (verify->md5_server && verify->sha1_server) { + crypto_hash_update(verify->md5_server, buf, len); + crypto_hash_update(verify->sha1_server, buf, len); + } + if (verify->md5_cert && verify->sha1_cert) { + crypto_hash_update(verify->md5_cert, buf, len); + crypto_hash_update(verify->sha1_cert, buf, len); + } +} + + +void tls_verify_hash_free(struct tls_verify_hash *verify) +{ + crypto_hash_finish(verify->md5_client, NULL, NULL); + crypto_hash_finish(verify->md5_server, NULL, NULL); + crypto_hash_finish(verify->md5_cert, NULL, NULL); + crypto_hash_finish(verify->sha1_client, NULL, NULL); + crypto_hash_finish(verify->sha1_server, NULL, NULL); + crypto_hash_finish(verify->sha1_cert, NULL, NULL); + verify->md5_client = NULL; + verify->md5_server = NULL; + verify->md5_cert = NULL; + verify->sha1_client = NULL; + verify->sha1_server = NULL; + verify->sha1_cert = NULL; +} diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h new file mode 100644 index 0000000000000..77505649a2528 --- /dev/null +++ b/src/tls/tlsv1_common.h @@ -0,0 +1,216 @@ +/* + * TLSv1 common definitions + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_COMMON_H +#define TLSV1_COMMON_H + +#include "crypto.h" + +#define TLS_VERSION 0x0301 /* TLSv1 */ +#define TLS_RANDOM_LEN 32 +#define TLS_PRE_MASTER_SECRET_LEN 48 +#define TLS_MASTER_SECRET_LEN 48 +#define TLS_SESSION_ID_MAX_LEN 32 +#define TLS_VERIFY_DATA_LEN 12 + +/* HandshakeType */ +enum { + TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0, + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1, + TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2, + TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE = 11, + TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12, + TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13, + TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14, + TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15, + TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16, + TLS_HANDSHAKE_TYPE_FINISHED = 20, + TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */ +}; + +/* CipherSuite */ +#define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */ +#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */ +#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */ +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */ +#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */ +#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */ +#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */ +#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */ +#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */ +#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */ +#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */ +#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */ +#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */ +#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */ +#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ + +/* CompressionMethod */ +#define TLS_COMPRESSION_NULL 0 + +/* AlertLevel */ +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +/* AlertDescription */ +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS_ALERT_BAD_RECORD_MAC 20 +#define TLS_ALERT_DECRYPTION_FAILED 21 +#define TLS_ALERT_RECORD_OVERFLOW 22 +#define TLS_ALERT_DECOMPRESSION_FAILURE 30 +#define TLS_ALERT_HANDSHAKE_FAILURE 40 +#define TLS_ALERT_BAD_CERTIFICATE 42 +#define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define TLS_ALERT_CERTIFICATE_REVOKED 44 +#define TLS_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS_ALERT_ILLEGAL_PARAMETER 47 +#define TLS_ALERT_UNKNOWN_CA 48 +#define TLS_ALERT_ACCESS_DENIED 49 +#define TLS_ALERT_DECODE_ERROR 50 +#define TLS_ALERT_DECRYPT_ERROR 51 +#define TLS_ALERT_EXPORT_RESTRICTION 60 +#define TLS_ALERT_PROTOCOL_VERSION 70 +#define TLS_ALERT_INSUFFICIENT_SECURITY 71 +#define TLS_ALERT_INTERNAL_ERROR 80 +#define TLS_ALERT_USER_CANCELED 90 +#define TLS_ALERT_NO_RENEGOTIATION 100 +#define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */ +#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */ +#define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */ + +/* ChangeCipherSpec */ +enum { + TLS_CHANGE_CIPHER_SPEC = 1 +}; + +/* TLS Extensions */ +#define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */ +#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */ +#define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */ +#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ +#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ +#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ + +#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ + + +typedef enum { + TLS_KEY_X_NULL, + TLS_KEY_X_RSA, + TLS_KEY_X_RSA_EXPORT, + TLS_KEY_X_DH_DSS_EXPORT, + TLS_KEY_X_DH_DSS, + TLS_KEY_X_DH_RSA_EXPORT, + TLS_KEY_X_DH_RSA, + TLS_KEY_X_DHE_DSS_EXPORT, + TLS_KEY_X_DHE_DSS, + TLS_KEY_X_DHE_RSA_EXPORT, + TLS_KEY_X_DHE_RSA, + TLS_KEY_X_DH_anon_EXPORT, + TLS_KEY_X_DH_anon +} tls_key_exchange; + +typedef enum { + TLS_CIPHER_NULL, + TLS_CIPHER_RC4_40, + TLS_CIPHER_RC4_128, + TLS_CIPHER_RC2_CBC_40, + TLS_CIPHER_IDEA_CBC, + TLS_CIPHER_DES40_CBC, + TLS_CIPHER_DES_CBC, + TLS_CIPHER_3DES_EDE_CBC, + TLS_CIPHER_AES_128_CBC, + TLS_CIPHER_AES_256_CBC +} tls_cipher; + +typedef enum { + TLS_HASH_NULL, + TLS_HASH_MD5, + TLS_HASH_SHA +} tls_hash; + +struct tls_cipher_suite { + u16 suite; + tls_key_exchange key_exchange; + tls_cipher cipher; + tls_hash hash; +}; + +typedef enum { + TLS_CIPHER_STREAM, + TLS_CIPHER_BLOCK +} tls_cipher_type; + +struct tls_cipher_data { + tls_cipher cipher; + tls_cipher_type type; + size_t key_material; + size_t expanded_key_material; + size_t block_size; /* also iv_size */ + enum crypto_cipher_alg alg; +}; + + +struct tls_verify_hash { + struct crypto_hash *md5_client; + struct crypto_hash *sha1_client; + struct crypto_hash *md5_server; + struct crypto_hash *sha1_server; + struct crypto_hash *md5_cert; + struct crypto_hash *sha1_cert; +}; + + +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite); +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher); +int tls_server_key_exchange_allowed(tls_cipher cipher); +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk); +int tls_verify_hash_init(struct tls_verify_hash *verify); +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len); +void tls_verify_hash_free(struct tls_verify_hash *verify); + +#endif /* TLSV1_COMMON_H */ diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c new file mode 100644 index 0000000000000..d5564672c8e17 --- /dev/null +++ b/src/tls/tlsv1_cred.c @@ -0,0 +1,422 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "crypto.h" +#include "x509v3.h" +#include "tlsv1_cred.h" + + +struct tlsv1_credentials * tlsv1_cred_alloc(void) +{ + struct tlsv1_credentials *cred; + cred = os_zalloc(sizeof(*cred)); + return cred; +} + + +void tlsv1_cred_free(struct tlsv1_credentials *cred) +{ + if (cred == NULL) + return; + + x509_certificate_chain_free(cred->trusted_certs); + x509_certificate_chain_free(cred->cert); + crypto_private_key_free(cred->key); + os_free(cred->dh_p); + os_free(cred->dh_g); + os_free(cred); +} + + +static int tlsv1_add_cert_der(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + struct x509_certificate *cert; + char name[128]; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", + __func__); + return -1; + } + + cert->next = *chain; + *chain = cert; + + x509_name_string(&cert->subject, name, sizeof(name)); + wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); + + return 0; +} + + +static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; +static const char *pem_cert_end = "-----END CERTIFICATE-----"; + + +static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) +{ + size_t i, plen; + + plen = os_strlen(tag); + if (len < plen) + return NULL; + + for (i = 0; i < len - plen; i++) { + if (os_memcmp(buf + i, tag, plen) == 0) + return buf + i; + } + + return NULL; +} + + +static int tlsv1_add_cert(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_cert_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " + "assume DER format"); + return tlsv1_add_cert_der(chain, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " + "DER format"); + + while (pos) { + pos += os_strlen(pem_cert_begin); + end = search_tag(pem_cert_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " + "certificate end tag (%s)", pem_cert_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " + "certificate"); + return -1; + } + + if (tlsv1_add_cert_der(chain, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " + "certificate after DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + end += os_strlen(pem_cert_end); + pos = search_tag(pem_cert_begin, end, buf + len - end); + } + + return 0; +} + + +static int tlsv1_set_cert_chain(struct x509_certificate **chain, + const char *cert, const u8 *cert_blob, + size_t cert_blob_len) +{ + if (cert_blob) + return tlsv1_add_cert(chain, cert_blob, cert_blob_len); + + if (cert) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(cert, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + cert); + return -1; + } + + ret = tlsv1_add_cert(chain, buf, len); + os_free(buf); + return ret; + } + + return 0; +} + + +/** + * tlsv1_set_ca_cert - Set trusted CA certificate(s) + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: ca_cert_blob length + * @path: Path to CA certificates (not yet supported) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path) +{ + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, + cert_blob, cert_blob_len) < 0) + return -1; + + if (path) { + /* TODO: add support for reading number of certificate files */ + wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " + "not yet supported"); + return -1; + } + + return 0; +} + + +/** + * tlsv1_set_cert - Set certificate + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: cert_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len) +{ + return tlsv1_set_cert_chain(&cred->cert, cert, + cert_blob, cert_blob_len); +} + + +static int tlsv1_set_key(struct tlsv1_credentials *cred, + const u8 *key, size_t len) +{ + cred->key = crypto_private_key_import(key, len); + if (cred->key == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); + return -1; + } + return 0; +} + + +/** + * tlsv1_set_private_key - Set private key + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @private_key: File or reference name for the key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) +{ + crypto_private_key_free(cred->key); + cred->key = NULL; + + if (private_key_blob) + return tlsv1_set_key(cred, private_key_blob, + private_key_blob_len); + + if (private_key) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(private_key, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + private_key); + return -1; + } + + ret = tlsv1_set_key(cred, buf, len); + os_free(buf); + return ret; + } + + return 0; +} + + +static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, + const u8 *dh, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + pos = dh; + end = dh + len; + + /* + * DHParameter ::= SEQUENCE { + * prime INTEGER, -- p + * base INTEGER, -- g + * privateValueLength INTEGER OPTIONAL } + */ + + /* DHParamer ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " + "valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + /* prime INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_p); + cred->dh_p = os_malloc(hdr.length); + if (cred->dh_p == NULL) + return -1; + os_memcpy(cred->dh_p, hdr.payload, hdr.length); + cred->dh_p_len = hdr.length; + pos = hdr.payload + hdr.length; + + /* base INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_g); + cred->dh_g = os_malloc(hdr.length); + if (cred->dh_g == NULL) + return -1; + os_memcpy(cred->dh_g, hdr.payload, hdr.length); + cred->dh_g_len = hdr.length; + + return 0; +} + + +static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; +static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; + + +static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_dhparams_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " + "assume DER format"); + return tlsv1_set_dhparams_der(cred, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " + "format"); + + pos += os_strlen(pem_dhparams_begin); + end = search_tag(pem_dhparams_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " + "tag (%s)", pem_dhparams_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); + return -1; + } + + if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " + "DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + return 0; +} + + +/** + * tlsv1_set_dhparams - Set Diffie-Hellman parameters + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @dh_file: File or reference name for the DH params in PEM or DER format + * @dh_blob: DH params as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len) +{ + if (dh_blob) + return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); + + if (dh_file) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(dh_file, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + dh_file); + return -1; + } + + ret = tlsv1_set_dhparams_blob(cred, buf, len); + os_free(buf); + return ret; + } + + return 0; +} diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h new file mode 100644 index 0000000000000..8425fe4541266 --- /dev/null +++ b/src/tls/tlsv1_cred.h @@ -0,0 +1,46 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_CRED_H +#define TLSV1_CRED_H + +struct tlsv1_credentials { + struct x509_certificate *trusted_certs; + struct x509_certificate *cert; + struct crypto_private_key *key; + + /* Diffie-Hellman parameters */ + u8 *dh_p; /* prime */ + size_t dh_p_len; + u8 *dh_g; /* generator */ + size_t dh_g_len; +}; + + +struct tlsv1_credentials * tlsv1_cred_alloc(void); +void tlsv1_cred_free(struct tlsv1_credentials *cred); +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path); +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len); +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len); +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len); + +#endif /* TLSV1_CRED_H */ diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c new file mode 100644 index 0000000000000..f226ac3f9b378 --- /dev/null +++ b/src/tls/tlsv1_record.c @@ -0,0 +1,409 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" + + +/** + * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite + * @rl: Pointer to TLS record layer data + * @cipher_suite: New cipher suite + * Returns: 0 on success, -1 on failure + * + * This function is used to prepare TLS record layer for cipher suite change. + * tlsv1_record_change_write_cipher() and + * tlsv1_record_change_read_cipher() functions can then be used to change the + * currently used ciphers. + */ +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite) +{ + const struct tls_cipher_suite *suite; + const struct tls_cipher_data *data; + + wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", + cipher_suite); + rl->cipher_suite = cipher_suite; + + suite = tls_get_cipher_suite(cipher_suite); + if (suite == NULL) + return -1; + + if (suite->hash == TLS_HASH_MD5) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; + rl->hash_size = MD5_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; + rl->hash_size = SHA1_MAC_LEN; + } + + data = tls_get_cipher_data(suite->cipher); + if (data == NULL) + return -1; + + rl->key_material_len = data->key_material; + rl->iv_size = data->block_size; + rl->cipher_alg = data->alg; + + return 0; +} + + +/** + * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for writing. + */ +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " + "0x%04x", rl->cipher_suite); + rl->write_cipher_suite = rl->cipher_suite; + os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->write_cbc) { + crypto_cipher_deinit(rl->write_cbc); + rl->write_cbc = NULL; + } + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->write_cbc = crypto_cipher_init(rl->cipher_alg, + rl->write_iv, rl->write_key, + rl->key_material_len); + if (rl->write_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for reading. + */ +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " + "0x%04x", rl->cipher_suite); + rl->read_cipher_suite = rl->cipher_suite; + os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->read_cbc) { + crypto_cipher_deinit(rl->read_cbc); + rl->read_cbc = NULL; + } + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->read_cbc = crypto_cipher_init(rl->cipher_alg, + rl->read_iv, rl->read_key, + rl->key_material_len); + if (rl->read_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_send - TLS record layer: Send a message + * @rl: Pointer to TLS record layer data + * @content_type: Content type (TLS_CONTENT_TYPE_*) + * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the + * beginning for record layer to fill in; payload filled in after this and + * extra space in the end for HMAC). + * @buf_size: Maximum buf size + * @payload_len: Length of the payload + * @out_len: Buffer for returning the used buf length + * Returns: 0 on success, -1 on failure + * + * This function fills in the TLS record layer header, adds HMAC, and encrypts + * the data using the current write cipher. + */ +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, size_t payload_len, size_t *out_len) +{ + u8 *pos, *ct_start, *length, *payload; + struct crypto_hash *hmac; + size_t clen; + + pos = buf; + /* ContentType type */ + ct_start = pos; + *pos++ = content_type; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* uint16 length */ + length = pos; + WPA_PUT_BE16(length, payload_len); + pos += 2; + + /* opaque fragment[TLSPlaintext.length] */ + payload = pos; + pos += payload_len; + + if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, + rl->hash_size); + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + return -1; + } + crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, ct_start, pos - ct_start); + clen = buf + buf_size - pos; + if (clen < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " + "enough room for MAC"); + crypto_hash_finish(hmac, NULL, NULL); + return -1; + } + + if (crypto_hash_finish(hmac, pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", + pos, clen); + pos += clen; + if (rl->iv_size) { + size_t len = pos - payload; + size_t pad; + pad = (len + 1) % rl->iv_size; + if (pad) + pad = rl->iv_size - pad; + if (pos + pad + 1 > buf + buf_size) { + wpa_printf(MSG_DEBUG, "TLSv1: No room for " + "block cipher padding"); + return -1; + } + os_memset(pos, pad, pad + 1); + pos += pad + 1; + } + + if (crypto_cipher_encrypt(rl->write_cbc, payload, + payload, pos - payload) < 0) + return -1; + } + + WPA_PUT_BE16(length, pos - length - 2); + inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); + + *out_len = pos - buf; + + return 0; +} + + +/** + * tlsv1_record_receive - TLS record layer: Process a received message + * @rl: Pointer to TLS record layer data + * @in_data: Received data + * @in_len: Length of the received data + * @out_data: Buffer for output data (must be at least as long as in_data) + * @out_len: Set to maximum out_data length by caller; used to return the + * length of the used data + * @alert: Buffer for returning an alert value on failure + * Returns: 0 on success, -1 on failure + * + * This function decrypts the received message, verifies HMAC and TLS record + * layer header. + */ +int tlsv1_record_receive(struct tlsv1_record_layer *rl, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t *out_len, u8 *alert) +{ + size_t i, rlen, hlen; + u8 padlen; + struct crypto_hash *hmac; + u8 len[2], hash[100]; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, in_len); + + if (in_len < TLS_RECORD_HEADER_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", + (unsigned long) in_len); + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " + "length %d", in_data[0], in_data[1], in_data[2], + WPA_GET_BE16(in_data + 3)); + + if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && + in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && + in_data[0] != TLS_CONTENT_TYPE_ALERT && + in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", + in_data[0]); + *alert = TLS_ALERT_UNEXPECTED_MESSAGE; + return -1; + } + + if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " + "%d.%d", in_data[1], in_data[2]); + *alert = TLS_ALERT_PROTOCOL_VERSION; + return -1; + } + + rlen = WPA_GET_BE16(in_data + 3); + + /* TLSCiphertext must not be more than 2^14+2048 bytes */ + if (TLS_RECORD_HEADER_LEN + rlen > 18432) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + in_data += TLS_RECORD_HEADER_LEN; + in_len -= TLS_RECORD_HEADER_LEN; + + if (rlen > in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " + "(rlen=%lu > in_len=%lu)", + (unsigned long) rlen, (unsigned long) in_len); + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + + in_len = rlen; + + if (*out_len < in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " + "processing received record"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + os_memcpy(out_data, in_data, in_len); + *out_len = in_len; + + if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + if (crypto_cipher_decrypt(rl->read_cbc, out_data, + out_data, in_len) < 0) { + *alert = TLS_ALERT_DECRYPTION_FAILED; + return -1; + } + if (rl->iv_size) { + if (in_len == 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record" + " (no pad)"); + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + padlen = out_data[in_len - 1]; + if (padlen >= in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " + "length (%u, in_len=%lu) in " + "received record", + padlen, (unsigned long) in_len); + *alert = TLS_ALERT_DECRYPTION_FAILED; + return -1; + } + for (i = in_len - padlen; i < in_len; i++) { + if (out_data[i] != padlen) { + wpa_hexdump(MSG_DEBUG, + "TLSv1: Invalid pad in " + "received record", + out_data + in_len - padlen, + padlen); + *alert = TLS_ALERT_DECRYPTION_FAILED; + return -1; + } + } + + *out_len -= padlen + 1; + } + + wpa_hexdump(MSG_MSGDUMP, + "TLSv1: Record Layer - Decrypted data", + out_data, in_len); + + if (*out_len < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " + "hash value"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + *out_len -= rl->hash_size; + + hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, + rl->hash_size); + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); + WPA_PUT_BE16(len, *out_len); + crypto_hash_update(hmac, len, 2); + crypto_hash_update(hmac, out_data, *out_len); + hlen = sizeof(hash); + if (crypto_hash_finish(hmac, hash, &hlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); + return -1; + } + if (hlen != rl->hash_size || + os_memcmp(hash, out_data + *out_len, hlen) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " + "received message"); + *alert = TLS_ALERT_BAD_RECORD_MAC; + return -1; + } + } + + /* TLSCompressed must not be more than 2^14+1024 bytes */ + if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); + + return 0; +} diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h new file mode 100644 index 0000000000000..9170fb1a2fe45 --- /dev/null +++ b/src/tls/tlsv1_record.h @@ -0,0 +1,74 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_RECORD_H +#define TLSV1_RECORD_H + +#include "crypto.h" + +#define TLS_MAX_WRITE_MAC_SECRET_LEN 20 +#define TLS_MAX_WRITE_KEY_LEN 32 +#define TLS_MAX_IV_LEN 16 +#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ + TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN)) + +#define TLS_SEQ_NUM_LEN 8 +#define TLS_RECORD_HEADER_LEN 5 + +/* ContentType */ +enum { + TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20, + TLS_CONTENT_TYPE_ALERT = 21, + TLS_CONTENT_TYPE_HANDSHAKE = 22, + TLS_CONTENT_TYPE_APPLICATION_DATA = 23 +}; + +struct tlsv1_record_layer { + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 write_key[TLS_MAX_WRITE_KEY_LEN]; + u8 read_key[TLS_MAX_WRITE_KEY_LEN]; + u8 write_iv[TLS_MAX_IV_LEN]; + u8 read_iv[TLS_MAX_IV_LEN]; + + size_t hash_size; + size_t key_material_len; + size_t iv_size; /* also block_size */ + + enum crypto_hash_alg hash_alg; + enum crypto_cipher_alg cipher_alg; + + u8 write_seq_num[TLS_SEQ_NUM_LEN]; + u8 read_seq_num[TLS_SEQ_NUM_LEN]; + + u16 cipher_suite; + u16 write_cipher_suite; + u16 read_cipher_suite; + + struct crypto_cipher *write_cbc; + struct crypto_cipher *read_cbc; +}; + + +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite); +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, size_t payload_len, size_t *out_len); +int tlsv1_record_receive(struct tlsv1_record_layer *rl, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t *out_len, u8 *alert); + +#endif /* TLSV1_RECORD_H */ diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c new file mode 100644 index 0000000000000..c204a4778a74f --- /dev/null +++ b/src/tls/tlsv1_server.c @@ -0,0 +1,596 @@ +/* + * TLSv1 server (RFC 2246) + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(pre_master_secret, pre_master_secret_len, + "master secret", seed, 2 * TLS_RANDOM_LEN, + conn->master_secret, TLS_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "key expansion", seed, 2 * TLS_RANDOM_LEN, + key_block, key_block_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + /* client_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + + return 0; +} + + +/** + * tlsv1_server_handshake - Process TLS handshake + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * Returns: Pointer to output data, %NULL on failure + */ +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + const u8 *pos, *end; + u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + size_t in_msg_len; + + if (in_data == NULL || in_len == 0) { + wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); + return NULL; + } + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + if (tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert)) { + wpa_printf(MSG_DEBUG, "TLSv1: Processing received " + "record failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } + ct = pos[0]; + + in_pos = in_msg; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_server_process_handshake(conn, ct, in_pos, + &in_msg_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + } + + os_free(in_msg); + in_msg = NULL; + + msg = tlsv1_server_handshake_write(conn, out_len); + +failed: + os_free(in_msg); + if (conn->alert_level) { + if (conn->state == FAILED) { + /* Avoid alert loops */ + wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop"); + os_free(msg); + return NULL; + } + conn->state = FAILED; + os_free(msg); + msg = tlsv1_server_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } + + return msg; +} + + +/** + * tlsv1_server_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + out_data, out_len, in_len, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_server_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + const u8 *in_end, *pos; + int res; + u8 alert, *out_end, *out_pos; + size_t olen; + + pos = in_data; + in_end = in_data + in_len; + out_pos = out_data; + out_end = out_data + out_len; + + while (pos < in_end) { + if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x", pos[0]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + olen = out_end - out_pos; + res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + out_pos += olen; + if (out_pos > out_end) { + wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " + "for processing the received record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + } + + return out_pos - out_data; +} + + +/** + * tlsv1_server_global_init - Initialize TLSv1 server + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 server functions. + */ +int tlsv1_server_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_server_global_deinit - Deinitialize TLSv1 server + * + * This function can be used to deinitialize the TLSv1 server that was + * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions + * can be called after this before calling tlsv1_server_global_init() again. + */ +void tlsv1_server_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_server_init - Initialize TLSv1 server connection + * @cred: Pointer to server credentials from tlsv1_server_cred_alloc() + * Returns: Pointer to TLSv1 server connection data or %NULL on failure + */ +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) +{ + struct tlsv1_server *conn; + size_t count; + u16 *suites; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->cred = cred; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; +#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; +#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + conn->num_cipher_suites = count; + + return conn; +} + + +static void tlsv1_server_clear_data(struct tlsv1_server *conn) +{ + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + + crypto_public_key_free(conn->client_rsa_key); + conn->client_rsa_key = NULL; + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + conn->session_ticket_len = 0; + conn->use_session_ticket = 0; + + os_free(conn->dh_secret); + conn->dh_secret = NULL; + conn->dh_secret_len = 0; +} + + +/** + * tlsv1_server_deinit - Deinitialize TLSv1 server connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + */ +void tlsv1_server_deinit(struct tlsv1_server *conn) +{ + tlsv1_server_clear_data(conn); + os_free(conn); +} + + +/** + * tlsv1_server_established - Check whether connection has been established + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_server_established(struct tlsv1_server *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_server_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_server_get_cipher - Get current cipher name + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen) +{ + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + cipher = "ADH-AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + cipher = "AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + default: + return -1; + } + + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; + return 0; +} + + +/** + * tlsv1_server_shutdown - Shutdown TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_shutdown(struct tlsv1_server *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_server_clear_data(conn); + + return 0; +} + + +/** + * tlsv1_server_resumed - Was session resumption used + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_server_resumed(struct tlsv1_server *conn) +{ + return 0; +} + + +/** + * tlsv1_server_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_server_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_server_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) +{ +#ifdef EAP_FAST + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; +#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; +#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; +#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; +#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +#else /* EAP_FAST */ + return -1; +#endif /* EAP_FAST */ +} + + +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer) +{ + conn->verify_peer = verify_peer; + return 0; +} + + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h new file mode 100644 index 0000000000000..00c536c3ee2b8 --- /dev/null +++ b/src/tls/tlsv1_server.h @@ -0,0 +1,54 @@ +/* + * TLSv1 server (RFC 2246) + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_SERVER_H +#define TLSV1_SERVER_H + +#include "tlsv1_cred.h" + +struct tlsv1_server; + +int tlsv1_server_global_init(void); +void tlsv1_server_global_deinit(void); +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); +void tlsv1_server_deinit(struct tlsv1_server *conn); +int tlsv1_server_established(struct tlsv1_server *conn); +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, size_t *out_len); +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen); +int tlsv1_server_shutdown(struct tlsv1_server *conn); +int tlsv1_server_resumed(struct tlsv1_server *conn); +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); + +typedef int (*tlsv1_server_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_SERVER_H */ diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h new file mode 100644 index 0000000000000..d11ea7559f3fe --- /dev/null +++ b/src/tls/tlsv1_server_i.h @@ -0,0 +1,77 @@ +/* + * TLSv1 server - internal structures + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLSV1_SERVER_I_H +#define TLSV1_SERVER_I_H + +struct tlsv1_server { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE, + CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + struct crypto_public_key *client_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 cipher_suite; + + struct tlsv1_credentials *cred; + + int verify_peer; + u16 client_version; + + u8 *session_ticket; + size_t session_ticket_len; + + tlsv1_server_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + int use_session_ticket; + + u8 *dh_secret; + size_t dh_secret_len; +}; + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len); +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len); +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len); +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len); + +#endif /* TLSV1_SERVER_I_H */ diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c new file mode 100644 index 0000000000000..0e299d8aa4e5e --- /dev/null +++ b/src/tls/tlsv1_server_read.c @@ -0,0 +1,1142 @@ +/* + * TLSv1 server - read handshake message + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "x509v3.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len); + + +static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end, *c; + size_t left, len, i, j; + u16 cipher_suite; + u16 num_suites; + int compr_null_found; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientHello)", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ClientHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len); + end = pos + len; + + /* ProtocolVersion client_version */ + if (end - pos < 2) + goto decode_error; + conn->client_version = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", + conn->client_version >> 8, conn->client_version & 0xff); + if (conn->client_version < TLS_VERSION) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "ClientHello"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_PROTOCOL_VERSION); + return -1; + } + pos += 2; + + /* Random random */ + if (end - pos < TLS_RANDOM_LEN) + goto decode_error; + + os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); + pos += 1 + *pos; + /* TODO: add support for session resumption */ + + /* CipherSuite cipher_suites<2..2^16-1> */ + if (end - pos < 2) + goto decode_error; + num_suites = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", + pos, num_suites); + if (num_suites & 1) + goto decode_error; + num_suites /= 2; + + cipher_suite = 0; + for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + c = pos; + for (j = 0; j < num_suites; j++) { + u16 tmp = WPA_GET_BE16(c); + c += 2; + if (!cipher_suite && tmp == conn->cipher_suites[i]) { + cipher_suite = tmp; + break; + } + } + } + pos += num_suites * 2; + if (!cipher_suite) { + wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " + "available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->cipher_suite = cipher_suite; + + /* CompressionMethod compression_methods<1..2^8-1> */ + if (end - pos < 1) + goto decode_error; + num_suites = *pos++; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", + pos, num_suites); + compr_null_found = 0; + for (i = 0; i < num_suites; i++) { + if (*pos++ == TLS_COMPRESSION_NULL) + compr_null_found = 1; + } + if (!compr_null_found) { + wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " + "compression"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (end - pos == 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " + "end of ClientHello: 0x%02x", *pos); + goto decode_error; + } + + if (end - pos >= 2) { + u16 ext_len; + + /* Extension client_hello_extension_list<0..2^16-1> */ + + ext_len = WPA_GET_BE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " + "extensions", ext_len); + if (end - pos != ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " + "extension list length %u (expected %u)", + ext_len, end - pos); + goto decode_error; + } + + /* + * struct { + * ExtensionType extension_type (0..65535) + * opaque extension_data<0..2^16-1> + * } Extension; + */ + + while (pos < end) { + u16 ext_type, ext_len; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_type field"); + goto decode_error; + } + + ext_type = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data length field"); + goto decode_error; + } + + ext_len = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data field"); + goto decode_error; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " + "type %u", ext_type); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " + "Extension data", pos, ext_len); + + if (ext_type == TLS_EXT_SESSION_TICKET) { + os_free(conn->session_ticket); + conn->session_ticket = os_malloc(ext_len); + if (conn->session_ticket) { + os_memcpy(conn->session_ticket, pos, + ext_len); + conn->session_ticket_len = ext_len; + } + } + + pos += ext_len; + } + } + + *in_len = end - in_data; + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " + "ServerHello"); + conn->state = SERVER_HELLO; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "Certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_client_key_exchange(conn, ct, in_data, + in_len); + } + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->client_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->client_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason) < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_client_key_exchange_rsa( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ + u8 *out; + size_t outlen, outbuflen; + u16 encr_len; + int res; + int use_random = 0; + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + encr_len = WPA_GET_BE16(pos); + pos += 2; + + outbuflen = outlen = end - pos; + out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? + outlen : TLS_PRE_MASTER_SECRET_LEN); + if (out == NULL) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* + * struct { + * ProtocolVersion client_version; + * opaque random[46]; + * } PreMasterSecret; + * + * struct { + * public-key-encrypted PreMasterSecret pre_master_secret; + * } EncryptedPreMasterSecret; + */ + + /* + * Note: To avoid Bleichenbacher attack, we do not report decryption or + * parsing errors from EncryptedPreMasterSecret processing to the + * client. Instead, a random pre-master secret is used to force the + * handshake to fail. + */ + + if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, + pos, end - pos, + out, &outlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " + "PreMasterSecret (encr_len=%d outlen=%lu)", + end - pos, (unsigned long) outlen); + use_random = 1; + } + + if (outlen != TLS_PRE_MASTER_SECRET_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " + "length %lu", (unsigned long) outlen); + use_random = 1; + } + + if (WPA_GET_BE16(out) != conn->client_version) { + wpa_printf(MSG_DEBUG, "TLSv1: Client version in " + "ClientKeyExchange does not match with version in " + "ClientHello"); + use_random = 1; + } + + if (use_random) { + wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret " + "to avoid revealing information about private key"); + outlen = TLS_PRE_MASTER_SECRET_LEN; + if (os_get_random(out, outlen)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(out); + return -1; + } + } + + res = tlsv1_server_derive_keys(conn, out, outlen); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(out, 0, outbuflen); + os_free(out); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +} + + +static int tls_process_client_key_exchange_dh_anon( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ +#ifdef EAP_FAST + const u8 *dh_yc; + u16 dh_yc_len; + u8 *shared; + size_t shared_len; + int res; + + /* + * struct { + * select (PublicValueEncoding) { + * case implicit: struct { }; + * case explicit: opaque dh_Yc<1..2^16-1>; + * } dh_public; + * } ClientDiffieHellmanPublic; + */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", + pos, end - pos); + + if (end == pos) { + wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding " + "not supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " + "length"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + dh_yc_len = WPA_GET_BE16(pos); + dh_yc = pos + 2; + + if (dh_yc + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " + "(length %d)", dh_yc_len); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + shared_len = conn->cred->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* shared = Yc^secret mod p */ + if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, + conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + shared, &shared_len)) { + os_free(shared); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(conn->dh_secret, 0, conn->dh_secret_len); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + + res = tlsv1_server_derive_keys(conn, shared, shared_len); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(shared, 0, shared_len); + os_free(shared); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +#else /* EAP_FAST */ + return -1; +#endif /* EAP_FAST */ +} + + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " + "(Left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + + wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (keyx == TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + return -1; + + if (keyx != TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_rsa(conn, pos, end) < 0) + return -1; + + *in_len = end - in_data; + + conn->state = CERTIFICATE_VERIFY; + + return 0; +} + + +static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + size_t hlen, buflen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + u16 slen; + + if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_change_cipher_spec(conn, ct, in_data, + in_len); + } + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " + "message (len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " + "message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateVerify)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + + /* + * struct { + * Signature signature; + * } CertificateVerify; + */ + + hpos = hash; + + if (alg == SIGN_ALG_RSA) { + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (conn->client_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " + "signature"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, + pos, end - pos, buf, &buflen) < 0) + { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + buf, buflen); + + if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " + "CertificateVerify - did not match with calculated " + "hash"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + os_free(buf); + + *in_len = end - in_data; + + conn->state = CHANGE_CIPHER_SPEC; + + return 0; +} + + +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + + conn->state = CLIENT_FINISHED; + + return 0; +} + + +static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_client == NULL || + crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " + "successfully"); + conn->state = ESTABLISHED; + } else { + /* Full handshake */ + conn->state = SERVER_CHANGE_CIPHER_SPEC; + } + + return 0; +} + + +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + switch (conn->state) { + case CLIENT_HELLO: + if (tls_process_client_hello(conn, ct, buf, len)) + return -1; + break; + case CLIENT_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case CLIENT_KEY_EXCHANGE: + if (tls_process_client_key_exchange(conn, ct, buf, len)) + return -1; + break; + case CERTIFICATE_VERIFY: + if (tls_process_certificate_verify(conn, ct, buf, len)) + return -1; + break; + case CHANGE_CIPHER_SPEC: + if (tls_process_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case CLIENT_FINISHED: + if (tls_process_client_finished(conn, ct, buf, len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) + tls_verify_hash_add(&conn->verify, buf, *len); + + return 0; +} diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c new file mode 100644 index 0000000000000..cf54f4265c3a7 --- /dev/null +++ b/src/tls/tlsv1_server_write.c @@ -0,0 +1,796 @@ +/* + * TLSv1 server - write handshake message + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "x509v3.h" +#include "tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + + +static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +static int tls_write_server_hello(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + struct os_time now; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + os_get_time(&now); + WPA_PUT_BE32(conn->server_random, now.sec); + if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "server_random"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + conn->session_id_len = TLS_SESSION_ID_MAX_LEN; + if (os_get_random(conn->session_id, conn->session_id_len)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "session_id"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ServerHello */ + /* ProtocolVersion server_version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suite */ + WPA_PUT_BE16(pos, conn->cipher_suite); + pos += 2; + /* CompressionMethod compression_method */ + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->session_ticket && conn->session_ticket_cb) { + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, + conn->session_ticket, conn->session_ticket_len, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = res; + + if (conn->use_session_ticket) { + if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } + + /* + * RFC 4507 specifies that server would include an empty + * SessionTicket extension in ServerHello and a + * NewSessionTicket message after the ServerHello. However, + * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket + * extension at the moment, does not use such extensions. + * + * TODO: Add support for configuring RFC 4507 behavior and make + * EAP-FAST disable it. + */ + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_certificate(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when " + "using anonymous DH"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred->cert; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (cert == conn->cred->cert || cert == NULL) { + /* + * Server was not configured with all the needed certificates + * to form a full certificate chain. The client may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_key_exchange(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; +#ifdef EAP_FAST + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + u8 *dh_ys; + size_t dh_ys_len; +#endif /* EAP_FAST */ + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed"); + return 0; + } + + if (keyx != TLS_KEY_X_DH_anon) { + /* TODO? */ + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " + "supported with key exchange type %d", keyx); + return -1; + } + +#ifdef EAP_FAST + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->cred->dh_g == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " + "ServerKeyExhcange"); + return -1; + } + + os_free(conn->dh_secret); + conn->dh_secret_len = conn->cred->dh_p_len; + conn->dh_secret = os_malloc(conn->dh_secret_len); + if (conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for secret (Diffie-Hellman)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (os_get_random(conn->dh_secret, conn->dh_secret_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data for Diffie-Hellman"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + return -1; + } + + if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > + 0) + conn->dh_secret[0] = 0; /* make sure secret < p */ + + pos = conn->dh_secret; + while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0) + pos++; + if (pos != conn->dh_secret) { + os_memmove(conn->dh_secret, pos, + conn->dh_secret_len - (pos - conn->dh_secret)); + conn->dh_secret_len -= pos - conn->dh_secret; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value", + conn->dh_secret, conn->dh_secret_len); + + /* Ys = g^secret mod p */ + dh_ys_len = conn->cred->dh_p_len; + dh_ys = os_malloc(dh_ys_len); + if (dh_ys == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " + "Diffie-Hellman"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, + conn->dh_secret, conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + dh_ys, &dh_ys_len)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + dh_ys, dh_ys_len); + + /* + * struct { + * select (KeyExchangeAlgorithm) { + * case diffie_hellman: + * ServerDHParams params; + * Signature signed_params; + * case rsa: + * ServerRSAParams params; + * Signature signed_params; + * }; + * } ServerKeyExchange; + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - ServerDHParams */ + /* dh_p */ + if (pos + 2 + conn->cred->dh_p_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_p"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_p_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); + pos += conn->cred->dh_p_len; + + /* dh_g */ + if (pos + 2 + conn->cred->dh_g_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_g"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_g_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len); + pos += conn->cred->dh_g_len; + + /* dh_Ys */ + if (pos + 2 + dh_ys_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_Ys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, dh_ys_len); + pos += 2; + os_memcpy(pos, dh_ys, dh_ys_len); + pos += dh_ys_len; + os_free(dh_ys); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +#else /* EAP_FAST */ + return -1; +#endif /* EAP_FAST */ +} + + +static int tls_write_server_certificate_request(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - CertificateRequest */ + + /* + * enum { + * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4), + * (255) + * } ClientCertificateType; + * ClientCertificateType certificate_types<1..2^8-1> + */ + *pos++ = 1; + *pos++ = 1; /* rsa_sign */ + + /* + * opaque DistinguishedName<1..2^16-1> + * DistinguishedName certificate_authorities<3..2^16-1> + */ + /* TODO: add support for listing DNs for trusted CAs */ + WPA_PUT_BE16(pos, 0); + pos += 2; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_hello_done(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ServerHelloDone (empty) */ + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + *pos = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + rhdr, end - rhdr, 1, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos = rhdr + rlen; + + return 0; +} + + +static int tls_write_server_finished(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + + /* Encrypted Handshake Message: Finished */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_server == NULL || + crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + + if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", + verify_data, TLS_VERIFY_DATA_LEN); + + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); + pos += TLS_VERIFY_DATA_LEN; + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos = rhdr + rlen; + + *msgpos = pos; + + return 0; +} + + +static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (tls_write_server_hello(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CHANGE_CIPHER_SPEC; + + return msg; + } + + /* Full handshake */ + if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_key_exchange(conn, &pos, end) < 0 || + tls_write_server_certificate_request(conn, &pos, end) < 0 || + tls_write_server_hello_done(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CLIENT_CERTIFICATE; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) +{ + switch (conn->state) { + case SERVER_HELLO: + return tls_send_server_hello(conn, out_len); + case SERVER_CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + default: + if (conn->state == ESTABLISHED && conn->use_session_ticket) { + /* Abbreviated handshake was already completed. */ + return NULL; + } + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c new file mode 100644 index 0000000000000..59bf4ff0524a7 --- /dev/null +++ b/src/tls/x509v3.c @@ -0,0 +1,1724 @@ +/* + * X.509v3 certificate parsing and processing (RFC 3280 profile) + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" + +#ifdef CONFIG_INTERNAL_X509 + +#include "asn1.h" +#include "crypto.h" +#include "x509v3.h" + + +static void x509_free_name(struct x509_name *name) +{ + os_free(name->cn); + os_free(name->c); + os_free(name->l); + os_free(name->st); + os_free(name->o); + os_free(name->ou); + os_free(name->email); + name->cn = name->c = name->l = name->st = name->o = name->ou = NULL; + name->email = NULL; +} + + +/** + * x509_certificate_free - Free an X.509 certificate + * @cert: Certificate to be freed + */ +void x509_certificate_free(struct x509_certificate *cert) +{ + if (cert == NULL) + return; + if (cert->next) { + wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " + "was still on a list (next=%p)\n", + cert, cert->next); + } + x509_free_name(&cert->issuer); + x509_free_name(&cert->subject); + os_free(cert->public_key); + os_free(cert->sign_value); + os_free(cert); +} + + +/** + * x509_certificate_free - Free an X.509 certificate chain + * @cert: Pointer to the first certificate in the chain + */ +void x509_certificate_chain_free(struct x509_certificate *cert) +{ + struct x509_certificate *next; + + while (cert) { + next = cert->next; + cert->next = NULL; + x509_certificate_free(cert); + cert = next; + } +} + + +static int x509_whitespace(char c) +{ + return c == ' ' || c == '\t'; +} + + +static void x509_str_strip_whitespace(char *a) +{ + char *ipos, *opos; + int remove_whitespace = 1; + + ipos = opos = a; + + while (*ipos) { + if (remove_whitespace && x509_whitespace(*ipos)) + ipos++; + else { + remove_whitespace = x509_whitespace(*ipos); + *opos++ = *ipos++; + } + } + + *opos-- = '\0'; + if (opos > a && x509_whitespace(*opos)) + *opos = '\0'; +} + + +static int x509_str_compare(const char *a, const char *b) +{ + char *aa, *bb; + int ret; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + + aa = os_strdup(a); + bb = os_strdup(b); + + if (aa == NULL || bb == NULL) { + os_free(aa); + os_free(bb); + return os_strcasecmp(a, b); + } + + x509_str_strip_whitespace(aa); + x509_str_strip_whitespace(bb); + + ret = os_strcasecmp(aa, bb); + + os_free(aa); + os_free(bb); + + return ret; +} + + +/** + * x509_name_compare - Compare X.509 certificate names + * @a: Certificate name + * @b: Certificate name + * Returns: <0, 0, or >0 based on whether a is less than, equal to, or + * greater than b + */ +int x509_name_compare(struct x509_name *a, struct x509_name *b) +{ + int res; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + + res = x509_str_compare(a->cn, b->cn); + if (res) + return res; + res = x509_str_compare(a->c, b->c); + if (res) + return res; + res = x509_str_compare(a->l, b->l); + if (res) + return res; + res = x509_str_compare(a->st, b->st); + if (res) + return res; + res = x509_str_compare(a->o, b->o); + if (res) + return res; + res = x509_str_compare(a->ou, b->ou); + if (res) + return res; + res = x509_str_compare(a->email, b->email); + if (res) + return res; + + return 0; +} + + +static int x509_parse_algorithm_identifier( + const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = pos + hdr.length; + + if (end > buf + len) + return -1; + + *next = end; + + if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) + return -1; + + /* TODO: optional parameters */ + + return 0; +} + + +static int x509_parse_public_key(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + */ + + pos = buf; + end = buf + len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(SubjectPublicKeyInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > end) + return -1; + end = pos + hdr.length; + *next = end; + + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->public_key_alg, &pos)) + return -1; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(subjectPublicKey) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length < 1) + return -1; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* + * TODO: should this be rejected? X.509 certificates are + * unlikely to use such a construction. Now we would end up + * including the extra bits in the buffer which may also be + * ok. + */ + } + os_free(cert->public_key); + cert->public_key = os_malloc(hdr.length - 1); + if (cert->public_key == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "public key"); + return -1; + } + os_memcpy(cert->public_key, pos + 1, hdr.length - 1); + cert->public_key_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", + cert->public_key, cert->public_key_len); + + return 0; +} + + +static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; + struct asn1_oid oid; + char **fieldp; + + /* + * Name ::= CHOICE { RDNSequence } + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Name / RDNSequencer) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > buf + len) + return -1; + + end = *next = pos + hdr.length; + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, "X509: Expected SET " + "(RelativeDistinguishedName) - found class " + "%d tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + set_pos = hdr.payload; + pos = set_end = hdr.payload + hdr.length; + + if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AttributeTypeAndValue) - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + seq_pos = hdr.payload; + seq_end = hdr.payload + hdr.length; + + if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { + x509_free_name(name); + return -1; + } + + if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "AttributeValue"); + x509_free_name(name); + return -1; + } + + /* RFC 3280: + * MUST: country, organization, organizational-unit, + * distinguished name qualifier, state or province name, + * common name, serial number. + * SHOULD: locality, title, surname, given name, initials, + * pseudonym, generation qualifier. + * MUST: domainComponent (RFC 2247). + */ + fieldp = NULL; + if (oid.len == 4 && + oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { + /* id-at ::= 2.5.4 */ + switch (oid.oid[3]) { + case 3: + /* commonName */ + fieldp = &name->cn; + break; + case 6: + /* countryName */ + fieldp = &name->c; + break; + case 7: + /* localityName */ + fieldp = &name->l; + break; + case 8: + /* stateOrProvinceName */ + fieldp = &name->st; + break; + case 10: + /* organizationName */ + fieldp = &name->o; + break; + case 11: + /* organizationalUnitName */ + fieldp = &name->ou; + break; + } + } else if (oid.len == 7 && + oid.oid[0] == 1 && oid.oid[1] == 2 && + oid.oid[2] == 840 && oid.oid[3] == 113549 && + oid.oid[4] == 1 && oid.oid[5] == 9 && + oid.oid[6] == 1) { + /* 1.2.840.113549.1.9.1 - e-mailAddress */ + fieldp = &name->email; + } + + if (fieldp == NULL) { + wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", + (u8 *) oid.oid, + oid.len * sizeof(oid.oid[0])); + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", + hdr.payload, hdr.length); + continue; + } + + os_free(*fieldp); + *fieldp = os_malloc(hdr.length + 1); + if (*fieldp == NULL) { + x509_free_name(name); + return -1; + } + os_memcpy(*fieldp, hdr.payload, hdr.length); + (*fieldp)[hdr.length] = '\0'; + } + + return 0; +} + + +/** + * x509_name_string - Convert an X.509 certificate name into a string + * @name: Name to convert + * @buf: Buffer for the string + * @len: Maximum buffer length + */ +void x509_name_string(struct x509_name *name, char *buf, size_t len) +{ + char *pos, *end; + int ret; + + if (len == 0) + return; + + pos = buf; + end = buf + len; + + if (name->c) { + ret = os_snprintf(pos, end - pos, "C=%s, ", name->c); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + if (name->st) { + ret = os_snprintf(pos, end - pos, "ST=%s, ", name->st); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + if (name->l) { + ret = os_snprintf(pos, end - pos, "L=%s, ", name->l); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + if (name->o) { + ret = os_snprintf(pos, end - pos, "O=%s, ", name->o); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + if (name->ou) { + ret = os_snprintf(pos, end - pos, "OU=%s, ", name->ou); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + if (name->cn) { + ret = os_snprintf(pos, end - pos, "CN=%s, ", name->cn); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + + if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { + *pos-- = '\0'; + *pos-- = '\0'; + } + + if (name->email) { + ret = os_snprintf(pos, end - pos, "/emailAddress=%s", + name->email); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + +done: + end[-1] = '\0'; +} + + +static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, + os_time_t *val) +{ + const char *pos; + int year, month, day, hour, min, sec; + + /* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + * + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ + + pos = (const char *) buf; + + switch (asn1_tag) { + case ASN1_TAG_UTCTIME: + if (len != 13 || buf[12] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "UTCTime format", buf, len); + return -1; + } + if (sscanf(pos, "%02d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "UTCTime year", buf, len); + return -1; + } + if (year < 50) + year += 2000; + else + year += 1900; + pos += 2; + break; + case ASN1_TAG_GENERALIZEDTIME: + if (len != 15 || buf[14] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "GeneralizedTime format", buf, len); + return -1; + } + if (sscanf(pos, "%04d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "GeneralizedTime year", buf, len); + return -1; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " + "GeneralizedTime - found tag 0x%x", asn1_tag); + return -1; + } + + if (sscanf(pos, "%02d", &month) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(month)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &day) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(day)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &hour) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(hour)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &min) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(min)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &sec) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(sec)", buf, len); + return -1; + } + + if (os_mktime(year, month, day, hour, min, sec, val) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", + buf, len); + if (year < 1970) { + /* + * At least some test certificates have been configured + * to use dates prior to 1970. Set the date to + * beginning of 1970 to handle these case. + */ + wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " + "assume epoch as the time", year); + *val = 0; + return 0; + } + return -1; + } + + return 0; +} + + +static int x509_parse_validity(const u8 *buf, size_t len, + struct x509_certificate *cert, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos; + size_t plen; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time + * } + * + * RFC 3280, 4.1.2.5: + * CAs conforming to this profile MUST always encode certificate + * validity dates through the year 2049 as UTCTime; certificate + * validity dates in 2050 or later MUST be encoded as GeneralizedTime. + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Validity) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + plen = hdr.length; + + if (pos + plen > buf + len) + return -1; + + *next = pos + plen; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_before) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " + "Time", hdr.payload, hdr.length); + return -1; + } + + pos = hdr.payload + hdr.length; + plen = *next - pos; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_after) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " + "Time", hdr.payload, hdr.length); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", + (unsigned long) cert->not_before, + (unsigned long) cert->not_after); + + return 0; +} + + +static int x509_id_ce_oid(struct asn1_oid *oid) +{ + /* id-ce arc from X.509 for standard X.509v3 extensions */ + return oid->len >= 4 && + oid->oid[0] == 2 /* joint-iso-ccitt */ && + oid->oid[1] == 5 /* ds */ && + oid->oid[2] == 29 /* id-ce */; +} + + +static int x509_parse_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING || + hdr.length < 1) { + wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " + "KeyUsage; found %d tag 0x%x len %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + + cert->extensions_present |= X509_EXT_KEY_USAGE; + cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); + + wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); + + return 0; +} + + +static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + unsigned long value; + size_t left; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "BasicConstraints; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; + + if (hdr.length == 0) + return 0; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u) in BasicConstraints", + hdr.length); + return -1; + } + cert->ca = hdr.payload[0]; + + if (hdr.payload + hdr.length == pos + len) { + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", + cert->ca); + return 0; + } + + if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, + &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + } + + if (hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " + "BasicConstraints; found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->path_len_constraint = value; + cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; + + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " + "pathLenConstraint=%lu", + cert->ca, cert->path_len_constraint); + + return 0; +} + + +static int x509_parse_extension_data(struct x509_certificate *cert, + struct asn1_oid *oid, + const u8 *pos, size_t len) +{ + if (!x509_id_ce_oid(oid)) + return 1; + + /* TODO: add other extensions required by RFC 3280, Ch 4.2: + * certificate policies (section 4.2.1.5) + * the subject alternative name (section 4.2.1.7) + * name constraints (section 4.2.1.11) + * policy constraints (section 4.2.1.12) + * extended key usage (section 4.2.1.13) + * inhibit any-policy (section 4.2.1.15) + */ + switch (oid->oid[3]) { + case 15: /* id-ce-keyUsage */ + return x509_parse_ext_key_usage(cert, pos, len); + case 19: /* id-ce-basicConstraints */ + return x509_parse_ext_basic_constraints(cert, pos, len); + default: + return 1; + } +} + + +static int x509_parse_extension(struct x509_certificate *cert, + const u8 *pos, size_t len, const u8 **next) +{ + const u8 *end; + struct asn1_hdr hdr; + struct asn1_oid oid; + int critical_ext = 0, res; + char buf[80]; + + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected SEQUENCE", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + *next = end = pos + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " + "Extension (expected OID)"); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + (hdr.tag != ASN1_TAG_BOOLEAN && + hdr.tag != ASN1_TAG_OCTETSTRING)) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected BOOLEAN " + "or OCTET STRING", hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u)", hdr.length); + return -1; + } + critical_ext = hdr.payload[0]; + pos = hdr.payload; + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + (hdr.class != ASN1_CLASS_UNIVERSAL && + hdr.class != ASN1_CLASS_PRIVATE) || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " + "in Extensions: class %d tag 0x%x; " + "expected OCTET STRING", + hdr.class, hdr.tag); + return -1; + } + } + + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", + buf, critical_ext); + wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); + + res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); + if (res < 0) + return res; + if (res == 1 && critical_ext) { + wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", + buf); + return -1; + } + + return 0; +} + + +static int x509_parse_extensions(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + const u8 *end; + struct asn1_hdr hdr; + + /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " + "for Extensions: class %d tag 0x%x; " + "expected SEQUENCE", hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (pos < end) { + if (x509_parse_extension(cert, pos, end - pos, &pos) + < 0) + return -1; + } + + return 0; +} + + +static int x509_parse_tbs_certificate(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + size_t left; + char sbuf[128]; + unsigned long value; + + /* tbsCertificate TBSCertificate ::= SEQUENCE */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " + "with a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = *next = pos + hdr.length; + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + pos = hdr.payload; + + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "version field - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected version field " + "length %u (expected 1)", hdr.length); + return -1; + } + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->version = value; + if (cert->version != X509_CERT_V1 && + cert->version != X509_CERT_V2 && + cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", + cert->version + 1); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + } else + cert->version = X509_CERT_V1; + wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "serialNumber; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + while (left) { + cert->serial_number <<= 8; + cert->serial_number |= *pos++; + left--; + } + wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); + + /* signature AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, + &pos)) + return -1; + + /* issuer Name */ + if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) + return -1; + x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); + wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); + + /* validity Validity */ + if (x509_parse_validity(pos, end - pos, cert, &pos)) + return -1; + + /* subject Name */ + if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) + return -1; + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); + + /* subjectPublicKeyInfo SubjectPublicKeyInfo */ + if (x509_parse_public_key(pos, end - pos, cert, &pos)) + return -1; + + if (pos == end) + return 0; + + if (cert->version == X509_CERT_V1) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == 1) { + /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag == 2) { + /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag != 3) { + wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " + "Context-Specific tag %d in optional " + "tbsCertificate fields", hdr.tag); + return 0; + } + + /* extensions [3] EXPLICIT Extensions OPTIONAL */ + + if (cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " + "Extensions data which are only allowed for " + "version 3", cert->version + 1); + return -1; + } + + if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) + return -1; + + pos = hdr.payload + hdr.length; + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "X509: Ignored extra tbsCertificate data", + pos, end - pos); + } + + return 0; +} + + +static int x509_rsadsi_oid(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int x509_pkcs_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 1 /* pkcs */; +} + + +static int x509_digest_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 2 /* digestAlgorithm */; +} + + +static int x509_sha1_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 14 /* oiw */ && + oid->oid[3] == 3 /* secsig */ && + oid->oid[4] == 2 /* algorithms */ && + oid->oid[5] == 26 /* id-sha1 */; +} + + +static int x509_sha256_oid(struct asn1_oid *oid) +{ + return oid->len == 9 && + oid->oid[0] == 2 /* joint-iso-itu-t */ && + oid->oid[1] == 16 /* country */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 1 /* organization */ && + oid->oid[4] == 101 /* gov */ && + oid->oid[5] == 3 /* csor */ && + oid->oid[6] == 4 /* nistAlgorithm */ && + oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[8] == 1 /* sha256 */; +} + + +/** + * x509_certificate_parse - Parse a X.509 certificate in DER format + * @buf: Pointer to the X.509 certificate in DER format + * @len: Buffer length + * Returns: Pointer to the parsed certificate or %NULL on failure + * + * Caller is responsible for freeing the returned certificate by calling + * x509_certificate_free(). + */ +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *hash_start; + struct x509_certificate *cert; + + cert = os_zalloc(sizeof(*cert) + len); + if (cert == NULL) + return NULL; + os_memcpy(cert + 1, buf, len); + cert->cert_start = (u8 *) (cert + 1); + cert->cert_len = len; + + pos = buf; + end = buf + len; + + /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ + + /* Certificate ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " + "a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + + if (pos + hdr.length > end) { + x509_certificate_free(cert); + return NULL; + } + + if (pos + hdr.length < end) { + wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " + "encoded certificate", + pos + hdr.length, end - pos + hdr.length); + end = pos + hdr.length; + } + + hash_start = pos; + cert->tbs_cert_start = cert->cert_start + (hash_start - buf); + if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { + x509_certificate_free(cert); + return NULL; + } + cert->tbs_cert_len = pos - hash_start; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->signature_alg, &pos)) { + x509_certificate_free(cert); + return NULL; + } + + /* signatureValue BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(signatureValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + if (hdr.length < 1) { + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + x509_certificate_free(cert); + return NULL; + } + os_free(cert->sign_value); + cert->sign_value = os_malloc(hdr.length - 1); + if (cert->sign_value == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "signatureValue"); + x509_certificate_free(cert); + return NULL; + } + os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); + cert->sign_value_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: signature", + cert->sign_value, cert->sign_value_len); + + return cert; +} + + +/** + * x509_certificate_check_signature - Verify certificate signature + * @issuer: Issuer certificate + * @cert: Certificate to be verified + * Returns: 0 if cert has a valid signature that was signed by the issuer, + * -1 if not + */ +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert) +{ + struct crypto_public_key *pk; + u8 *data; + const u8 *pos, *end, *next, *da_end; + size_t data_len; + struct asn1_hdr hdr; + struct asn1_oid oid; + u8 hash[32]; + size_t hash_len; + + if (!x509_pkcs_oid(&cert->signature.oid) || + cert->signature.oid.len != 7 || + cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " + "algorithm"); + return -1; + } + + pk = crypto_public_key_import(issuer->public_key, + issuer->public_key_len); + if (pk == NULL) + return -1; + + data_len = cert->sign_value_len; + data = os_malloc(data_len); + if (data == NULL) { + crypto_public_key_free(pk); + return -1; + } + + if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, + cert->sign_value_len, data, + &data_len) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); + crypto_public_key_free(pk); + os_free(data); + return -1; + } + crypto_public_key_free(pk); + + wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); + + /* + * PKCS #1 v1.5, 10.1.2: + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + * + */ + if (asn1_get_next(data, data_len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(DigestInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* + * X.509: + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + da_end = hdr.payload + hdr.length; + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); + os_free(data); + return -1; + } + + if (x509_sha1_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 5 /* sha-1WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha256_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 11 /* sha2561WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (!x509_digest_oid(&oid)) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); + os_free(data); + return -1; + } + switch (oid.oid[5]) { + case 5: /* md5 */ + if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) + { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " + "not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + break; + case 2: /* md2 */ + case 4: /* md4 */ + default: + wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " + "(%lu)", oid.oid[5]); + os_free(data); + return -1; + } + +skip_digest_oid: + /* Digest ::= OCTET STRING */ + pos = da_end; + end = data + data_len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " + "(Digest) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", + hdr.payload, hdr.length); + + switch (cert->signature.oid.oid[6]) { + case 4: /* md5WithRSAEncryption */ + md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 16; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", + hash, hash_len); + break; + case 5: /* sha-1WithRSAEncryption */ + sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 20; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", + hash, hash_len); + break; + case 11: /* sha256WithRSAEncryption */ +#ifdef NEED_SHA256 + sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 32; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", + hash, hash_len); + break; +#else /* NEED_SHA256 */ + wpa_printf(MSG_INFO, "X509: SHA256 support disabled"); + os_free(data); + return -1; +#endif /* NEED_SHA256 */ + case 2: /* md2WithRSAEncryption */ + case 12: /* sha384WithRSAEncryption */ + case 13: /* sha512WithRSAEncryption */ + default: + wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " + "algorithm (%lu)", cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + + if (hdr.length != hash_len || + os_memcmp(hdr.payload, hash, hdr.length) != 0) { + wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " + "with calculated tbsCertificate hash"); + os_free(data); + return -1; + } + + os_free(data); + + wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " + "calculated tbsCertificate hash"); + + return 0; +} + + +static int x509_valid_issuer(const struct x509_certificate *cert) +{ + if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && + !cert->ca) { + wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " + "issuer"); + return -1; + } + + if (cert->version == X509_CERT_V3 && + !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { + wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " + "include BasicConstraints extension"); + return -1; + } + + if ((cert->extensions_present & X509_EXT_KEY_USAGE) && + !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { + wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " + "keyCertSign bit in Key Usage"); + return -1; + } + + return 0; +} + + +/** + * x509_certificate_chain_validate - Validate X.509 certificate chain + * @trusted: List of trusted certificates + * @chain: Certificate chain to be validated (first chain must be issued by + * signed by the second certificate in the chain and so on) + * @reason: Buffer for returning failure reason (X509_VALIDATE_*) + * Returns: 0 if chain is valid, -1 if not + */ +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason) +{ + long unsigned idx; + int chain_trusted = 0; + struct x509_certificate *cert, *trust; + char buf[128]; + struct os_time now; + + *reason = X509_VALIDATE_OK; + + wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); + os_get_time(&now); + + for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + x509_name_string(&cert->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); + + if (chain_trusted) + continue; + + if ((unsigned long) now.sec < + (unsigned long) cert->not_before || + (unsigned long) now.sec > + (unsigned long) cert->not_after) { + wpa_printf(MSG_INFO, "X509: Certificate not valid " + "(now=%lu not_before=%lu not_after=%lu)", + now.sec, cert->not_before, cert->not_after); + *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; + return -1; + } + + if (cert->next) { + if (x509_name_compare(&cert->issuer, + &cert->next->subject) != 0) { + wpa_printf(MSG_DEBUG, "X509: Certificate " + "chain issuer name mismatch"); + x509_name_string(&cert->issuer, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", + buf); + x509_name_string(&cert->next->subject, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: next cert " + "subject: %s", buf); + *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; + return -1; + } + + if (x509_valid_issuer(cert->next) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if ((cert->next->extensions_present & + X509_EXT_PATH_LEN_CONSTRAINT) && + idx > cert->next->path_len_constraint) { + wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" + " not met (idx=%lu issuer " + "pathLenConstraint=%lu)", idx, + cert->next->path_len_constraint); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(cert->next, cert) + < 0) { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature within " + "chain"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + } + + for (trust = trusted; trust; trust = trust->next) { + if (x509_name_compare(&cert->issuer, &trust->subject) + == 0) + break; + } + + if (trust) { + wpa_printf(MSG_DEBUG, "X509: Found issuer from the " + "list of trusted certificates"); + if (x509_valid_issuer(trust) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(trust, cert) < 0) + { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: Trusted certificate " + "found to complete the chain"); + chain_trusted = 1; + } + } + + if (!chain_trusted) { + wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " + "from the list of trusted certificates"); + if (trusted) { + *reason = X509_VALIDATE_UNKNOWN_CA; + return -1; + } + wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " + "disabled - ignore unknown CA issue"); + } + + wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); + + return 0; +} + + +/** + * x509_certificate_get_subject - Get a certificate based on Subject name + * @chain: Certificate chain to search through + * @name: Subject name to search for + * Returns: Pointer to the certificate with the given Subject name or + * %NULL on failure + */ +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name) +{ + struct x509_certificate *cert; + + for (cert = chain; cert; cert = cert->next) { + if (x509_name_compare(&cert->subject, name) == 0) + return cert; + } + return NULL; +} + + +/** + * x509_certificate_self_signed - Is the certificate self-signed? + * @cert: Certificate + * Returns: 1 if certificate is self-signed, 0 if not + */ +int x509_certificate_self_signed(struct x509_certificate *cert) +{ + return x509_name_compare(&cert->issuer, &cert->subject) == 0; +} + +#endif /* CONFIG_INTERNAL_X509 */ diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h new file mode 100644 index 0000000000000..a52bcf8864538 --- /dev/null +++ b/src/tls/x509v3.h @@ -0,0 +1,154 @@ +/* + * X.509v3 certificate parsing and processing + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef X509V3_H +#define X509V3_H + +#include "asn1.h" + +struct x509_algorithm_identifier { + struct asn1_oid oid; +}; + +struct x509_name { + char *cn; /* commonName */ + char *c; /* countryName */ + char *l; /* localityName */ + char *st; /* stateOrProvinceName */ + char *o; /* organizationName */ + char *ou; /* organizationalUnitName */ + char *email; /* emailAddress */ +}; + +struct x509_certificate { + struct x509_certificate *next; + enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; + unsigned long serial_number; + struct x509_algorithm_identifier signature; + struct x509_name issuer; + struct x509_name subject; + os_time_t not_before; + os_time_t not_after; + struct x509_algorithm_identifier public_key_alg; + u8 *public_key; + size_t public_key_len; + struct x509_algorithm_identifier signature_alg; + u8 *sign_value; + size_t sign_value_len; + + /* Extensions */ + unsigned int extensions_present; +#define X509_EXT_BASIC_CONSTRAINTS (1 << 0) +#define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) +#define X509_EXT_KEY_USAGE (1 << 2) + + /* BasicConstraints */ + int ca; /* cA */ + unsigned long path_len_constraint; /* pathLenConstraint */ + + /* KeyUsage */ + unsigned long key_usage; +#define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0) +#define X509_KEY_USAGE_NON_REPUDIATION (1 << 1) +#define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2) +#define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3) +#define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4) +#define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5) +#define X509_KEY_USAGE_CRL_SIGN (1 << 6) +#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) +#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + + /* + * The DER format certificate follows struct x509_certificate. These + * pointers point to that buffer. + */ + const u8 *cert_start; + size_t cert_len; + const u8 *tbs_cert_start; + size_t tbs_cert_len; +}; + +enum { + X509_VALIDATE_OK, + X509_VALIDATE_BAD_CERTIFICATE, + X509_VALIDATE_UNSUPPORTED_CERTIFICATE, + X509_VALIDATE_CERTIFICATE_REVOKED, + X509_VALIDATE_CERTIFICATE_EXPIRED, + X509_VALIDATE_CERTIFICATE_UNKNOWN, + X509_VALIDATE_UNKNOWN_CA +}; + +#ifdef CONFIG_INTERNAL_X509 + +void x509_certificate_free(struct x509_certificate *cert); +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_name_string(struct x509_name *name, char *buf, size_t len); +int x509_name_compare(struct x509_name *a, struct x509_name *b); +void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert); +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason); +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name); +int x509_certificate_self_signed(struct x509_certificate *cert); + +#else /* CONFIG_INTERNAL_X509 */ + +static inline void x509_certificate_free(struct x509_certificate *cert) +{ +} + +static inline struct x509_certificate * +x509_certificate_parse(const u8 *buf, size_t len) +{ + return NULL; +} + +static inline void x509_name_string(struct x509_name *name, char *buf, + size_t len) +{ + if (len) + buf[0] = '\0'; +} + +static inline void x509_certificate_chain_free(struct x509_certificate *cert) +{ +} + +static inline int +x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason) +{ + return -1; +} + +static inline struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name) +{ + return NULL; +} + +static inline int x509_certificate_self_signed(struct x509_certificate *cert) +{ + return -1; +} + +#endif /* CONFIG_INTERNAL_X509 */ + +#endif /* X509V3_H */ diff --git a/src/utils/.gitignore b/src/utils/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/utils/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/utils/Makefile b/src/utils/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/utils/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/utils/base64.c b/src/utils/base64.c new file mode 100644 index 0000000000000..0eadb8142f662 --- /dev/null +++ b/src/utils/base64.c @@ -0,0 +1,187 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "os.h" +#include "base64.h" + +static const unsigned char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen; + int line_len; + + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen += olen / 72; /* line feeds */ + olen++; /* nul termination */ + out = os_malloc(olen); + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + line_len = 0; + while (end - in >= 3) { + *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[in[2] & 0x3f]; + in += 3; + line_len += 4; + if (line_len >= 72) { + *pos++ = '\n'; + line_len = 0; + } + } + + if (end - in) { + *pos++ = base64_table[in[0] >> 2]; + if (end - in == 1) { + *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } else { + *pos++ = base64_table[((in[0] & 0x03) << 4) | + (in[1] >> 4)]; + *pos++ = base64_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + line_len += 4; + } + + if (line_len) + *pos++ = '\n'; + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char dtable[256], *out, *pos, in[4], block[4], tmp; + size_t i, count, olen; + + os_memset(dtable, 0x80, 256); + for (i = 0; i < sizeof(base64_table) - 1; i++) + dtable[base64_table[i]] = (unsigned char) i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0 || count % 4) + return NULL; + + olen = count / 4 * 3; + pos = out = os_malloc(olen); + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len; i++) { + tmp = dtable[src[i]]; + if (tmp == 0x80) + continue; + + 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 + +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; + } + + buf = os_readfile(argv[2], &len); + if (buf == NULL) + return -1; + + 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/src/utils/base64.h b/src/utils/base64.h new file mode 100644 index 0000000000000..73312dde8f647 --- /dev/null +++ b/src/utils/base64.h @@ -0,0 +1,23 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef 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/hostapd/build_config.h b/src/utils/build_config.h index 58bcda825345e..1e147fe369113 100644 --- a/contrib/hostapd/build_config.h +++ b/src/utils/build_config.h @@ -44,7 +44,52 @@ #define EAP_GTC #define EAP_OTP #define EAP_LEAP +#define EAP_TNC #define _CRT_SECURE_NO_DEPRECATE + +#ifdef USE_INTERNAL_CRYPTO +#define CONFIG_TLS_INTERNAL +#define CONFIG_TLS_INTERNAL_CLIENT +#define CONFIG_INTERNAL_LIBTOMMATH +#define INTERNAL_AES +#define INTERNAL_SHA1 +#define INTERNAL_SHA256 +#define INTERNAL_MD5 +#define INTERNAL_MD4 +#define INTERNAL_DES +#define CONFIG_INTERNAL_X509 +#define CONFIG_CRYPTO_INTERNAL +#endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ +#ifdef __SYMBIAN32__ +#define OS_NO_C_LIB_DEFINES +#define CONFIG_ANSI_C_EXTRA +#define CONFIG_NO_WPA_MSG +#define CONFIG_NO_HOSTAPD_LOGGER +#define CONFIG_NO_STDOUT_DEBUG +#define CONFIG_BACKEND_FILE +#define INTERNAL_AES +#define INTERNAL_SHA1 +#define INTERNAL_MD5 +#define INTERNAL_MD4 +#define INTERNAL_DES +#define CONFIG_INTERNAL_LIBTOMMATH +#define CONFIG_INTERNAL_X509 +#define EAP_TLS_FUNCS +#define CONFIG_TLS_INTERNAL +#define CONFIG_CRYPTO_INTERNAL +#define IEEE8021X_EAPOL +#define PKCS12_FUNCS +#define EAP_MD5 +#define EAP_TLS +#define EAP_MSCHAPv2 +#define EAP_PEAP +#define EAP_TTLS +#define EAP_GTC +#define EAP_OTP +#define EAP_LEAP +#define EAP_FAST +#endif /* __SYMBIAN32__ */ + #endif /* BUILD_CONFIG_H */ diff --git a/contrib/hostapd/common.c b/src/utils/common.c index f226c68631b82..cb373c3debab8 100644 --- a/contrib/hostapd/common.c +++ b/src/utils/common.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,14 +17,6 @@ #include "common.h" -#ifdef CONFIG_DEBUG_FILE -static FILE *out_file = NULL; -#endif /* CONFIG_DEBUG_FILE */ -int wpa_debug_level = MSG_INFO; -int wpa_debug_show_keys = 0; -int wpa_debug_timestamp = 0; - - static int hex2num(char c) { if (c >= '0' && c <= '9') @@ -129,283 +121,19 @@ void wpa_get_ntp_timestamp(u8 *buf) { struct os_time now; u32 sec, usec; + be32 tmp; /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ os_get_time(&now); - sec = host_to_be32(now.sec + 2208988800U); /* Epoch to 1900 */ + sec = now.sec + 2208988800U; /* Epoch to 1900 */ /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ usec = now.usec; - usec = host_to_be32(4295 * usec - (usec >> 5) - (usec >> 9)); - os_memcpy(buf, (u8 *) &sec, 4); - os_memcpy(buf + 4, (u8 *) &usec, 4); -} - - - -#ifndef CONFIG_NO_STDOUT_DEBUG - -void wpa_debug_print_timestamp(void) -{ - struct os_time tv; - - if (!wpa_debug_timestamp) - return; - - os_get_time(&tv); -#ifdef CONFIG_DEBUG_FILE - if (out_file) { - fprintf(out_file, "%ld.%06u: ", (long) tv.sec, - (unsigned int) tv.usec); - } else -#endif /* CONFIG_DEBUG_FILE */ - printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); -} - - -/** - * 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; - - va_start(ap, fmt); - if (level >= wpa_debug_level) { - wpa_debug_print_timestamp(); -#ifdef CONFIG_DEBUG_FILE - if (out_file) { - vfprintf(out_file, fmt, ap); - fprintf(out_file, "\n"); - } else { -#endif /* CONFIG_DEBUG_FILE */ - vprintf(fmt, ap); - printf("\n"); -#ifdef CONFIG_DEBUG_FILE - } -#endif /* CONFIG_DEBUG_FILE */ - } - va_end(ap); -} - - -static void _wpa_hexdump(int level, const char *title, const u8 *buf, - size_t len, int show) -{ - size_t i; - if (level < wpa_debug_level) - return; - wpa_debug_print_timestamp(); -#ifdef CONFIG_DEBUG_FILE - if (out_file) { - fprintf(out_file, "%s - hexdump(len=%lu):", - title, (unsigned long) len); - if (buf == NULL) { - fprintf(out_file, " [NULL]"); - } else if (show) { - for (i = 0; i < len; i++) - fprintf(out_file, " %02x", buf[i]); - } else { - fprintf(out_file, " [REMOVED]"); - } - fprintf(out_file, "\n"); - } else { -#endif /* CONFIG_DEBUG_FILE */ - printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - if (buf == NULL) { - printf(" [NULL]"); - } else if (show) { - for (i = 0; i < len; i++) - printf(" %02x", buf[i]); - } else { - printf(" [REMOVED]"); - } - printf("\n"); -#ifdef CONFIG_DEBUG_FILE - } -#endif /* CONFIG_DEBUG_FILE */ -} - -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump(level, title, buf, len, 1); -} - - -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); -} - - -static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, - size_t len, int show) -{ - size_t i, llen; - const u8 *pos = buf; - const size_t line_len = 16; - - if (level < wpa_debug_level) - return; - wpa_debug_print_timestamp(); -#ifdef CONFIG_DEBUG_FILE - if (out_file) { - if (!show) { - fprintf(out_file, - "%s - hexdump_ascii(len=%lu): [REMOVED]\n", - title, (unsigned long) len); - return; - } - if (buf == NULL) { - fprintf(out_file, - "%s - hexdump_ascii(len=%lu): [NULL]\n", - title, (unsigned long) len); - return; - } - fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n", - title, (unsigned long) len); - while (len) { - llen = len > line_len ? line_len : len; - fprintf(out_file, " "); - for (i = 0; i < llen; i++) - fprintf(out_file, " %02x", pos[i]); - for (i = llen; i < line_len; i++) - fprintf(out_file, " "); - fprintf(out_file, " "); - for (i = 0; i < llen; i++) { - if (isprint(pos[i])) - fprintf(out_file, "%c", pos[i]); - else - fprintf(out_file, "_"); - } - for (i = llen; i < line_len; i++) - fprintf(out_file, " "); - fprintf(out_file, "\n"); - pos += llen; - len -= llen; - } - } else { -#endif /* CONFIG_DEBUG_FILE */ - if (!show) { - printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", - title, (unsigned long) len); - return; - } - if (buf == NULL) { - printf("%s - hexdump_ascii(len=%lu): [NULL]\n", - title, (unsigned long) len); - return; - } - printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); - while (len) { - llen = len > line_len ? line_len : len; - printf(" "); - for (i = 0; i < llen; i++) - printf(" %02x", pos[i]); - for (i = llen; i < line_len; i++) - printf(" "); - printf(" "); - for (i = 0; i < llen; i++) { - if (isprint(pos[i])) - printf("%c", pos[i]); - else - printf("_"); - } - for (i = llen; i < line_len; i++) - printf(" "); - printf("\n"); - pos += llen; - len -= llen; - } -#ifdef CONFIG_DEBUG_FILE - } -#endif /* CONFIG_DEBUG_FILE */ -} - - -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump_ascii(level, title, buf, len, 1); -} - - -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, - size_t len) -{ - _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); -} - - -int wpa_debug_open_file(const char *path) -{ -#ifdef CONFIG_DEBUG_FILE - if (!path) - return 0; - out_file = fopen(path, "a"); - if (out_file == NULL) { - wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " - "output file, using standard output"); - return -1; - } -#ifndef _WIN32 - setvbuf(out_file, NULL, _IOLBF, 0); -#endif /* _WIN32 */ -#endif /* CONFIG_DEBUG_FILE */ - return 0; -} - - -void wpa_debug_close_file(void) -{ -#ifdef CONFIG_DEBUG_FILE - if (!out_file) - return; - fclose(out_file); - out_file = NULL; -#endif /* CONFIG_DEBUG_FILE */ -} - -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -#ifndef CONFIG_NO_WPA_MSG -static wpa_msg_cb_func wpa_msg_cb = NULL; - -void wpa_msg_register_cb(wpa_msg_cb_func func) -{ - wpa_msg_cb = func; -} - - -void wpa_msg(void *ctx, int level, char *fmt, ...) -{ - va_list ap; - char *buf; - const int buflen = 2048; - int len; - - buf = os_malloc(buflen); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message " - "buffer"); - return; - } - va_start(ap, fmt); - len = vsnprintf(buf, buflen, fmt, ap); - va_end(ap); - wpa_printf(level, "%s", buf); - if (wpa_msg_cb) - wpa_msg_cb(ctx, level, buf, len); - os_free(buf); + usec = 4295 * usec - (usec >> 5) - (usec >> 9); + tmp = host_to_be32(sec); + os_memcpy(buf, (u8 *) &tmp, 4); + tmp = host_to_be32(usec); + os_memcpy(buf + 4, (u8 *) &tmp, 4); } -#endif /* CONFIG_NO_WPA_MSG */ static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, @@ -582,7 +310,7 @@ TCHAR * wpa_strdup_tchar(const char *str) * time, i.e., this is not re-entrant and the returned buffer must be used * before calling this again. */ -const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { static char ssid_txt[33]; char *pos; diff --git a/contrib/hostapd/common.h b/src/utils/common.h index eda328af345d8..d0a2eb3e6f90d 100644 --- a/contrib/hostapd/common.h +++ b/src/utils/common.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,6 +34,24 @@ #endif /* defined(__FreeBSD__) || defined(__NetBSD__) || * defined(__DragonFly__) */ +#ifdef __APPLE__ +#include <sys/types.h> +#include <machine/endian.h> +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +static inline unsigned short bswap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int bswap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} +#endif /* __APPLE__ */ + #ifdef CONFIG_TI_COMPILER #define __BIG_ENDIAN 4321 #define __LITTLE_ENDIAN 1234 @@ -44,6 +62,12 @@ #endif #endif /* CONFIG_TI_COMPILER */ +#ifdef __SYMBIAN32__ +#define __BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif /* __SYMBIAN32__ */ + #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -55,12 +79,90 @@ typedef int socklen_t; #endif /* CONFIG_NATIVE_WINDOWS */ -#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) - #ifdef _MSC_VER #define inline __inline + +#undef vsnprintf +#define vsnprintf _vsnprintf +#undef close +#define close closesocket +#endif /* _MSC_VER */ + + +/* Define platform specific integer types */ + +#ifdef _MSC_VER +typedef UINT64 u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef INT64 s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED #endif /* _MSC_VER */ +#ifdef __vxworks +typedef unsigned long long u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef long long s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED +#endif /* __vxworks */ + +#ifdef CONFIG_TI_COMPILER +#ifdef _LLONG_AVAILABLE +typedef unsigned long long u64; +#else +/* + * TODO: 64-bit variable not available. Using long as a workaround to test the + * build, but this will likely not work for all operations. + */ +typedef unsigned long u64; +#endif +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +#define WPA_TYPES_DEFINED +#endif /* CONFIG_TI_COMPILER */ + +#ifdef __SYMBIAN32__ +#define __REMOVE_PLATSEC_DIAGNOSTICS__ +#include <e32def.h> +typedef TUint64 u64; +typedef TUint32 u32; +typedef TUint16 u16; +typedef TUint8 u8; +#define WPA_TYPES_DEFINED +#endif /* __SYMBIAN32__ */ + +#ifndef WPA_TYPES_DEFINED +#ifdef CONFIG_USE_INTTYPES_H +#include <inttypes.h> +#else +#include <stdint.h> +#endif +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; +#define WPA_TYPES_DEFINED +#endif /* !WPA_TYPES_DEFINED */ + + +/* Define platform specific byte swapping macros */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + static inline unsigned short wpa_swap_16(unsigned short v) { return ((v & 0xff) << 8) | (v >> 8); @@ -80,7 +182,12 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define be_to_host32(n) wpa_swap_32(n) #define host_to_be32(n) wpa_swap_32(n) -#else /* __CYGWIN__ */ +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED #ifndef __BYTE_ORDER #ifndef __LITTLE_ENDIAN @@ -95,17 +202,18 @@ static inline unsigned int wpa_swap_32(unsigned int v) #endif /* __BYTE_ORDER */ #if __BYTE_ORDER == __LITTLE_ENDIAN -#define le_to_host16(n) (n) -#define host_to_le16(n) (n) -#define be_to_host16(n) bswap_16(n) -#define host_to_be16(n) bswap_16(n) -#define le_to_host32(n) (n) -#define be_to_host32(n) bswap_32(n) -#define host_to_be32(n) bswap_32(n) -#define le_to_host64(n) (n) -#define host_to_le64(n) (n) -#define be_to_host64(n) bswap_64(n) -#define host_to_be64(n) bswap_64(n) +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) #elif __BYTE_ORDER == __BIG_ENDIAN #define le_to_host16(n) bswap_16(n) #define host_to_le16(n) bswap_16(n) @@ -125,9 +233,12 @@ static inline unsigned int wpa_swap_32(unsigned int v) #error Could not determine CPU byte order #endif -#endif /* __CYGWIN__ */ +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ -/* Macros for handling unaligned 16-bit variables */ #define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) #define WPA_PUT_BE16(a, val) \ do { \ @@ -144,23 +255,37 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ ((u32) (a)[2])) -#define WPA_PUT_BE24(a, val) \ - do { \ - (a)[0] = (u8) (((u32) (val)) >> 16); \ - (a)[1] = (u8) (((u32) (val)) >> 8); \ - (a)[2] = (u8) (((u32) (val)) & 0xff); \ +#define WPA_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ } while (0) #define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ (((u32) (a)[2]) << 8) | ((u32) (a)[3])) -#define WPA_PUT_BE32(a, val) \ - do { \ - (a)[0] = (u8) (((u32) (val)) >> 24); \ - (a)[1] = (u8) (((u32) (val)) >> 16); \ - (a)[2] = (u8) (((u32) (val)) >> 8); \ - (a)[3] = (u8) (((u32) (val)) & 0xff); \ +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ } while (0) +#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define WPA_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) #define WPA_PUT_BE64(a, val) \ do { \ (a)[0] = (u8) (((u64) (val)) >> 56); \ @@ -173,74 +298,16 @@ static inline unsigned int wpa_swap_32(unsigned int v) (a)[7] = (u8) (((u64) (val)) & 0xff); \ } while (0) +#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + #ifndef ETH_ALEN #define ETH_ALEN 6 #endif -#ifdef _MSC_VER -typedef UINT64 u64; -typedef UINT32 u32; -typedef UINT16 u16; -typedef UINT8 u8; -typedef INT64 s64; -typedef INT32 s32; -typedef INT16 s16; -typedef INT8 s8; -#define WPA_TYPES_DEFINED -#endif /* _MSC_VER */ - -#ifdef __vxworks -typedef unsigned long long u64; -typedef UINT32 u32; -typedef UINT16 u16; -typedef UINT8 u8; -typedef long long s64; -typedef INT32 s32; -typedef INT16 s16; -typedef INT8 s8; -#define WPA_TYPES_DEFINED -#endif /* __vxworks */ - -#ifdef CONFIG_TI_COMPILER -#ifdef _LLONG_AVAILABLE -typedef unsigned long long u64; -#else -/* - * TODO: 64-bit variable not available. Using long as a workaround to test the - * build, but this will likely not work for all operations. - */ -typedef unsigned long u64; -#endif -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -#define WPA_TYPES_DEFINED -#endif /* CONFIG_TI_COMPILER */ - -#ifndef WPA_TYPES_DEFINED -#ifdef CONFIG_USE_INTTYPES_H -#include <inttypes.h> -#else -#include <stdint.h> -#endif -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; -typedef int64_t s64; -typedef int32_t s32; -typedef int16_t s16; -typedef int8_t s8; -#define WPA_TYPES_DEFINED -#endif /* !WPA_TYPES_DEFINED */ - -#define hostapd_get_rand os_get_random -int hwaddr_aton(const char *txt, u8 *addr); -int hexstr2bin(const char *hex, u8 *buf, size_t len); -void inc_byte_array(u8 *counter, size_t len); -void wpa_get_ntp_timestamp(u8 *buf); - #ifdef __GNUC__ #define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) @@ -251,173 +318,6 @@ void wpa_get_ntp_timestamp(u8 *buf); #endif -/* Debugging function - conditional printf and hex dump. Driver wrappers can - * use these for debugging purposes. */ - -enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; - -#ifdef CONFIG_NO_STDOUT_DEBUG - -#define wpa_debug_print_timestamp() do { } while (0) -#define wpa_printf(args...) do { } while (0) -#define wpa_hexdump(l,t,b,le) do { } while (0) -#define wpa_hexdump_key(l,t,b,le) do { } while (0) -#define wpa_hexdump_ascii(l,t,b,le) do { } while (0) -#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) -#define wpa_debug_open_file(p) do { } while (0) -#define wpa_debug_close_file() do { } while (0) - -#else /* CONFIG_NO_STDOUT_DEBUG */ - -int wpa_debug_open_file(const char *path); -void wpa_debug_close_file(void); - -/** - * wpa_debug_printf_timestamp - Print timestamp for debug output - * - * This function prints a timestamp in <seconds from 1970>.<microsoconds> - * format if debug output has been configured to include timestamps in debug - * messages. - */ -void wpa_debug_print_timestamp(void); - -/** - * wpa_printf - conditional printf - * @level: priority level (MSG_*) of the message - * @fmt: printf format string, followed by optional arguments - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. - * - * Note: New line '\n' is added to the end of the text when printing to stdout. - */ -void wpa_printf(int level, char *fmt, ...) -PRINTF_FORMAT(2, 3); - -/** - * wpa_hexdump - conditional hex dump - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump. - */ -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); - -/** - * wpa_hexdump_key - conditional hex dump, hide keys - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump. This works - * like wpa_hexdump(), but by default, does not include secret keys (passwords, - * etc.) in debug output. - */ -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); - -/** - * wpa_hexdump_ascii - conditional hex dump - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump with both - * the hex numbers and ASCII characters (for printable range) are shown. 16 - * bytes per line will be shown. - */ -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, - size_t len); - -/** - * wpa_hexdump_ascii_key - conditional hex dump, hide keys - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump with both - * the hex numbers and ASCII characters (for printable range) are shown. 16 - * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by - * default, does not include secret keys (passwords, etc.) in debug output. - */ -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, - size_t len); - -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -#ifdef CONFIG_NO_WPA_MSG -#define wpa_msg(args...) do { } while (0) -#define wpa_msg_register_cb(f) do { } while (0) -#else /* CONFIG_NO_WPA_MSG */ -/** - * wpa_msg - Conditional printf for default target and ctrl_iface monitors - * @ctx: Pointer to context data; this is the ctx variable registered - * with struct wpa_driver_ops::init() - * @level: priority level (MSG_*) of the message - * @fmt: printf format string, followed by optional arguments - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. This function is like wpa_printf(), but it also sends the - * same message to all attached ctrl_iface monitors. - * - * Note: New line '\n' is added to the end of the text when printing to stdout. - */ -void wpa_msg(void *ctx, int level, char *fmt, ...) PRINTF_FORMAT(3, 4); - -typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, - size_t len); - -/** - * wpa_msg_register_cb - Register callback function for wpa_msg() messages - * @func: Callback function (%NULL to unregister) - */ -void wpa_msg_register_cb(wpa_msg_cb_func func); -#endif /* CONFIG_NO_WPA_MSG */ - - -int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); -int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, - size_t len); - - -#ifdef EAPOL_TEST -#define WPA_ASSERT(a) \ - do { \ - if (!(a)) { \ - printf("WPA_ASSERT FAILED '" #a "' " \ - "%s %s:%d\n", \ - __FUNCTION__, __FILE__, __LINE__); \ - exit(1); \ - } \ - } while (0) -#else -#define WPA_ASSERT(a) do { } while (0) -#endif - - -#ifdef _MSC_VER -#undef vsnprintf -#define vsnprintf _vsnprintf -#undef close -#define close closesocket -#endif /* _MSC_VER */ - - #ifdef CONFIG_ANSI_C_EXTRA #if !defined(_MSC_VER) || _MSC_VER < 1400 @@ -474,7 +374,49 @@ void perror(const char *s); #endif /* CONFIG_ANSI_C_EXTRA */ -#define wpa_zalloc(s) os_zalloc((s)) +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +/* + * Definitions for sparse validation + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +#ifndef __must_check +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __must_check __attribute__((__warn_unused_result__)) +#else +#define __must_check +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +void inc_byte_array(u8 *counter, size_t len); +void wpa_get_ntp_timestamp(u8 *buf); +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len); #ifdef CONFIG_NATIVE_WINDOWS void wpa_unicode2ascii_inplace(TCHAR *str); @@ -484,9 +426,13 @@ TCHAR * wpa_strdup_tchar(const char *str); #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ -const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); + +static inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} -typedef u32 __be32; -typedef u64 __be64; +#include "wpa_debug.h" #endif /* COMMON_H */ diff --git a/contrib/hostapd/eloop.c b/src/utils/eloop.c index 232e7533cab28..4edb2a7033964 100644 --- a/contrib/hostapd/eloop.c +++ b/src/utils/eloop.c @@ -168,8 +168,18 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table, static void eloop_sock_table_destroy(struct eloop_sock_table *table) { - if (table) + if (table) { + int i; + for (i = 0; i < table->count && table->table; i++) { + printf("ELOOP: remaining socket: sock=%d " + "eloop_data=%p user_data=%p handler=%p\n", + table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data, + table->table[i].handler); + } os_free(table->table); + } } @@ -232,7 +242,10 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, timeout = os_malloc(sizeof(*timeout)); if (timeout == NULL) return -1; - os_get_time(&timeout->time); + if (os_get_time(&timeout->time) < 0) { + os_free(timeout); + return -1; + } timeout->time.sec += secs; timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { @@ -302,6 +315,25 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, } +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + tmp = eloop.timeout; + while (tmp != NULL) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + + tmp = tmp->next; + } + + return 0; +} + + #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { @@ -492,11 +524,25 @@ void eloop_terminate(void) void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; + struct os_time now; timeout = eloop.timeout; + if (timeout) + os_get_time(&now); while (timeout != NULL) { + int sec, usec; prev = timeout; timeout = timeout->next; + sec = prev->time.sec - now.sec; + usec = prev->time.usec - now.usec; + if (prev->time.usec < now.usec) { + sec--; + usec += 1000000; + } + printf("ELOOP: remaining timeout: %d.%06d eloop_data=%p " + "user_data=%p handler=%p\n", + sec, usec, prev->eloop_data, prev->user_data, + prev->handler); os_free(prev); } eloop_sock_table_destroy(&eloop.readers); diff --git a/contrib/hostapd/eloop.h b/src/utils/eloop.h index 4dd2871760df3..cf83f3836555e 100644 --- a/contrib/hostapd/eloop.h +++ b/src/utils/eloop.h @@ -207,6 +207,19 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data); /** + * eloop_is_timeout_registered - Check if a timeout is already registered + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is registered, 0 if the timeout is not registered + * + * Determine if a matching <handler,eloop_data,user_data> timeout is registered + * with eloop_register_timeout(). + */ +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** * eloop_register_signal - Register handler for signals * @sig: Signal number (e.g., SIGHUP) * @handler: Callback function to be called when the signal is received diff --git a/contrib/hostapd/eloop_none.c b/src/utils/eloop_none.c index 6943109d955fb..215030b2135f8 100644 --- a/contrib/hostapd/eloop_none.c +++ b/src/utils/eloop_none.c @@ -197,6 +197,26 @@ int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), } +int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx, + void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + tmp = eloop.timeout; + while (tmp != NULL) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + + tmp = tmp->next; + } + + return 0; +} + + /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) diff --git a/contrib/hostapd/eloop_win.c b/src/utils/eloop_win.c index 73f0eafeeb8b5..c95aa76b78829 100644 --- a/contrib/hostapd/eloop_win.c +++ b/src/utils/eloop_win.c @@ -320,6 +320,25 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, } +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + tmp = eloop.timeout; + while (tmp != NULL) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + + tmp = tmp->next; + } + + return 0; +} + + /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) @@ -444,12 +463,11 @@ void eloop_run(void) while (!eloop.terminate && (eloop.timeout || eloop.reader_count > 0 || eloop.event_count > 0)) { + tv.sec = tv.usec = 0; if (eloop.timeout) { os_get_time(&now); if (os_time_before(&now, &eloop.timeout->time)) os_time_sub(&eloop.timeout->time, &now, &tv); - else - tv.sec = tv.usec = 0; } count = 0; diff --git a/contrib/hostapd/includes.h b/src/utils/includes.h index 84308c3a37a25..63b5c23d84907 100644 --- a/contrib/hostapd/includes.h +++ b/src/utils/includes.h @@ -12,7 +12,7 @@ * See README and COPYING for more details. * * This header file is included into all C files so that commonly used header - * files can be selected with OS specific #ifdefs in one place instead of + * files can be selected with OS specific ifdef blocks in one place instead of * having to have OS/C library specific selection in many files. */ @@ -48,7 +48,9 @@ #include <netinet/in.h> #include <arpa/inet.h> #ifndef __vxworks +#ifndef __SYMBIAN32__ #include <sys/uio.h> +#endif /* __SYMBIAN32__ */ #include <sys/time.h> #endif /* __vxworks */ #endif /* CONFIG_TI_COMPILER */ diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c new file mode 100644 index 0000000000000..158fd57ed0170 --- /dev/null +++ b/src/utils/ip_addr.c @@ -0,0 +1,83 @@ +/* + * IP address processing + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ip_addr.h" + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen) +{ + if (buflen == 0 || addr == NULL) + return NULL; + + if (addr->af == AF_INET) { + os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen); + } else { + buf[0] = '\0'; + } +#ifdef CONFIG_IPV6 + if (addr->af == AF_INET6) { + if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) + buf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + + return buf; +} + + +int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b) +{ + if (a == NULL && b == NULL) + return 0; + if (a == NULL || b == NULL) + return 1; + + switch (a->af) { + case AF_INET: + if (a->u.v4.s_addr != b->u.v4.s_addr) + return 1; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0) + return 1; + break; +#endif /* CONFIG_IPV6 */ + } + + return 0; +} + + +int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) +{ +#ifndef CONFIG_NATIVE_WINDOWS + if (inet_aton(txt, &addr->u.v4)) { + addr->af = AF_INET; + return 0; + } + +#ifdef CONFIG_IPV6 + if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { + addr->af = AF_INET6; + return 0; + } +#endif /* CONFIG_IPV6 */ +#endif /* CONFIG_NATIVE_WINDOWS */ + + return -1; +} diff --git a/contrib/hostapd/config_types.h b/src/utils/ip_addr.h index ffcffa3c0cd47..192049a58311b 100644 --- a/contrib/hostapd/config_types.h +++ b/src/utils/ip_addr.h @@ -1,6 +1,6 @@ /* - * hostapd / Shared configuration file defines - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * IP address processing + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,8 +12,8 @@ * See README and COPYING for more details. */ -#ifndef CONFIG_TYPES_H -#define CONFIG_TYPES_H +#ifndef IP_ADDR_H +#define IP_ADDR_H struct hostapd_ip_addr { union { @@ -25,4 +25,9 @@ struct hostapd_ip_addr { int af; /* AF_INET / AF_INET6 */ }; -#endif /* CONFIG_TYPES_H */ +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen); +int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b); +int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr); + +#endif /* IP_ADDR_H */ diff --git a/contrib/hostapd/os.h b/src/utils/os.h index 25570a5e171ad..d6dfea682c2e9 100644 --- a/contrib/hostapd/os.h +++ b/src/utils/os.h @@ -456,6 +456,19 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #endif /* OS_NO_C_LIB_DEFINES */ +/** + * os_strlcpy - Copy a string with size bound and NUL-termination + * @dest: Destination + * @src: Source + * @siz: Size of the target buffer + * Returns: Total length of the target string (length of src) (not including + * NUL-termination) + * + * This function matches in behavior with the strlcpy(3) function in OpenBSD. + */ +size_t os_strlcpy(char *dest, const char *src, size_t siz); + + #ifdef OS_REJECT_C_LIB_FUNCTIONS #define malloc OS_DO_NOT_USE_malloc #define realloc OS_DO_NOT_USE_realloc diff --git a/contrib/hostapd/os_internal.c b/src/utils/os_internal.c index b2183ea38f4df..7b74bbf4abc7f 100644 --- a/contrib/hostapd/os_internal.c +++ b/src/utils/os_internal.c @@ -410,6 +410,31 @@ char * os_strncpy(char *dest, const char *src, size_t n) } +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} + + char * os_strstr(const char *haystack, const char *needle) { size_t len = os_strlen(needle); diff --git a/contrib/hostapd/os_none.c b/src/utils/os_none.c index 7404e2293af5d..bab8f178c05af 100644 --- a/contrib/hostapd/os_none.c +++ b/src/utils/os_none.c @@ -207,6 +207,12 @@ char * os_strncpy(char *dest, const char *src, size_t n) } +size_t os_strlcpy(char *dest, const char *src, size_t size) +{ + return 0; +} + + char * os_strstr(const char *haystack, const char *needle) { return NULL; diff --git a/contrib/hostapd/os_unix.c b/src/utils/os_unix.c index 69ba25a3e8566..060892d4034ec 100644 --- a/contrib/hostapd/os_unix.c +++ b/src/utils/os_unix.c @@ -76,9 +76,47 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +#ifdef __APPLE__ +#include <fcntl.h> +static int os_daemon(int nochdir, int noclose) +{ + int devnull; + + if (chdir("/") < 0) + return -1; + + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) + return -1; + + if (dup2(devnull, STDIN_FILENO) < 0) { + close(devnull); + return -1; + } + + if (dup2(devnull, STDOUT_FILENO) < 0) { + close(devnull); + return -1; + } + + if (dup2(devnull, STDERR_FILENO) < 0) { + close(devnull); + return -1; + } + + return 0; +} +#else /* __APPLE__ */ +#define os_daemon daemon +#endif /* __APPLE__ */ + + int os_daemonize(const char *pid_file) { - if (daemon(0, 0)) { +#ifdef __uClinux__ + return -1; +#else /* __uClinux__ */ + if (os_daemon(0, 0)) { perror("daemon"); return -1; } @@ -92,6 +130,7 @@ int os_daemonize(const char *pid_file) } return -0; +#endif /* __uClinux__ */ } @@ -188,7 +227,7 @@ int os_setenv(const char *name, const char *value, int overwrite) int os_unsetenv(const char *name) { -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) unsetenv(name); return 0; #else @@ -216,7 +255,12 @@ char * os_readfile(const char *name, size_t *len) return NULL; } - fread(buf, 1, *len, f); + if (fread(buf, 1, *len, f) != *len) { + fclose(f); + free(buf); + return NULL; + } + fclose(f); return buf; @@ -227,3 +271,28 @@ void * os_zalloc(size_t size) { return calloc(1, size); } + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c new file mode 100644 index 0000000000000..074096480a405 --- /dev/null +++ b/src/utils/os_win32.c @@ -0,0 +1,222 @@ +/* + * wpa_supplicant/hostapd / OS specific functions for Win32 systems + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <winsock2.h> +#include <wincrypt.h> + +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + Sleep(sec * 1000); + if (usec) + Sleep(usec / 1000); +} + + +int os_get_time(struct os_time *t) +{ +#define EPOCHFILETIME (116444736000000000ULL) + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG tt; + +#ifdef _WIN32_WCE + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); +#else /* _WIN32_WCE */ + GetSystemTimeAsFileTime(&ft); +#endif /* _WIN32_WCE */ + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + tt = (li.QuadPart - EPOCHFILETIME) / 10; + t->sec = (os_time_t) (tt / 1000000); + t->usec = (os_time_t) (tt % 1000000); + + return 0; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + struct tm tm, *tm1; + time_t t_local, t1, t2; + os_time_t tz_offset; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + t_local = mktime(&tm); + + /* figure out offset to UTC */ + tm1 = localtime(&t_local); + if (tm1) { + t1 = mktime(tm1); + tm1 = gmtime(&t_local); + if (tm1) { + t2 = mktime(tm1); + tz_offset = t2 - t1; + } else + tz_offset = 0; + } else + tz_offset = 0; + + *t = (os_time_t) t_local - tz_offset; + return 0; +} + + +int os_daemonize(const char *pid_file) +{ + /* TODO */ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; +} + + +unsigned long os_random(void) +{ + return rand(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return _strdup(rel_path); +} + + +int os_program_init(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + +void os_program_deinit(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + fread(buf, 1, *len, f); + fclose(f); + + return buf; +} + + +void * os_zalloc(size_t size) +{ + return calloc(1, size); +} + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c new file mode 100644 index 0000000000000..bf9f04a719b1d --- /dev/null +++ b/src/utils/pcsc_funcs.c @@ -0,0 +1,1238 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM + * cards through PC/SC smartcard library. These functions are used to implement + * authentication routines for EAP-SIM and EAP-AKA. + */ + +#include "includes.h" +#include <winscard.h> + +#include "common.h" +#include "pcsc_funcs.h" + + +/* See ETSI GSM 11.11 and ETSI TS 102 221 for details. + * SIM commands: + * Command APDU: CLA INS P1 P2 P3 Data + * CLA (class of instruction): A0 for GSM, 00 for USIM + * INS (instruction) + * P1 P2 P3 (parameters, P3 = length of Data) + * Response APDU: Data SW1 SW2 + * SW1 SW2 (Status words) + * Commands (INS P1 P2 P3): + * SELECT: A4 00 00 02 <file_id, 2 bytes> + * GET RESPONSE: C0 00 00 <len> + * RUN GSM ALG: 88 00 00 00 <RAND len = 10> + * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN + * P1 = ID of alg in card + * P2 = ID of secret key + * READ BINARY: B0 <offset high> <offset low> <len> + * READ RECORD: B2 <record number> <mode> <len> + * P2 (mode) = '02' (next record), '03' (previous record), + * '04' (absolute mode) + * VERIFY CHV: 20 00 <CHV number> 08 + * CHANGE CHV: 24 00 <CHV number> 10 + * DISABLE CHV: 26 00 01 08 + * ENABLE CHV: 28 00 01 08 + * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 + * SLEEP: FA 00 00 00 + */ + +/* GSM SIM commands */ +#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 +#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 +#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 +#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 +#define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 +#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 + +/* USIM commands */ +#define USIM_CLA 0x00 +#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 +#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 + +#define SIM_RECORD_MODE_ABSOLUTE 0x04 + +#define USIM_FSP_TEMPL_TAG 0x62 + +#define USIM_TLV_FILE_DESC 0x82 +#define USIM_TLV_FILE_ID 0x83 +#define USIM_TLV_DF_NAME 0x84 +#define USIM_TLV_PROPR_INFO 0xA5 +#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A +#define USIM_TLV_FILE_SIZE 0x80 +#define USIM_TLV_TOTAL_FILE_SIZE 0x81 +#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 +#define USIM_TLV_SHORT_FILE_ID 0x88 + +#define USIM_PS_DO_TAG 0x90 + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + + +typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; + +struct scard_data { + SCARDCONTEXT ctx; + SCARDHANDLE card; + DWORD protocol; + sim_types sim_type; + int pin1_required; +}; + +#ifdef __MINGW32_VERSION +/* MinGW does not yet support WinScard, so load the needed functions + * dynamically from winscard.dll for now. */ + +static HINSTANCE dll = NULL; /* winscard.dll */ + +static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; +#undef SCARD_PCI_T0 +#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) +#undef SCARD_PCI_T1 +#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) + + +static WINSCARDAPI LONG WINAPI +(*dll_SCardEstablishContext)(IN DWORD dwScope, + IN LPCVOID pvReserved1, + IN LPCVOID pvReserved2, + OUT LPSCARDCONTEXT phContext); +#define SCardEstablishContext dll_SCardEstablishContext + +static long (*dll_SCardReleaseContext)(long hContext); +#define SCardReleaseContext dll_SCardReleaseContext + +static WINSCARDAPI LONG WINAPI +(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, + IN LPCSTR mszGroups, + OUT LPSTR mszReaders, + IN OUT LPDWORD pcchReaders); +#undef SCardListReaders +#define SCardListReaders dll_SCardListReadersA + +static WINSCARDAPI LONG WINAPI +(*dll_SCardConnectA)(IN SCARDCONTEXT hContext, + IN LPCSTR szReader, + IN DWORD dwShareMode, + IN DWORD dwPreferredProtocols, + OUT LPSCARDHANDLE phCard, + OUT LPDWORD pdwActiveProtocol); +#undef SCardConnect +#define SCardConnect dll_SCardConnectA + +static WINSCARDAPI LONG WINAPI +(*dll_SCardDisconnect)(IN SCARDHANDLE hCard, + IN DWORD dwDisposition); +#define SCardDisconnect dll_SCardDisconnect + +static WINSCARDAPI LONG WINAPI +(*dll_SCardTransmit)(IN SCARDHANDLE hCard, + IN LPCSCARD_IO_REQUEST pioSendPci, + IN LPCBYTE pbSendBuffer, + IN DWORD cbSendLength, + IN OUT LPSCARD_IO_REQUEST pioRecvPci, + OUT LPBYTE pbRecvBuffer, + IN OUT LPDWORD pcbRecvLength); +#define SCardTransmit dll_SCardTransmit + +static WINSCARDAPI LONG WINAPI +(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); +#define SCardBeginTransaction dll_SCardBeginTransaction + +static WINSCARDAPI LONG WINAPI +(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); +#define SCardEndTransaction dll_SCardEndTransaction + + +static int mingw_load_symbols(void) +{ + char *sym; + + if (dll) + return 0; + + dll = LoadLibrary("winscard"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " + "library"); + return -1; + } + +#define LOADSYM(s) \ + sym = #s; \ + dll_ ## s = (void *) GetProcAddress(dll, sym); \ + if (dll_ ## s == NULL) \ + goto fail; + + LOADSYM(SCardEstablishContext); + LOADSYM(SCardReleaseContext); + LOADSYM(SCardListReadersA); + LOADSYM(SCardConnectA); + LOADSYM(SCardDisconnect); + LOADSYM(SCardTransmit); + LOADSYM(SCardBeginTransaction); + LOADSYM(SCardEndTransaction); + LOADSYM(g_rgSCardT0Pci); + LOADSYM(g_rgSCardT1Pci); + +#undef LOADSYM + + return 0; + +fail: + wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " + "winscard.dll", sym); + FreeLibrary(dll); + dll = NULL; + return -1; +} + + +static void mingw_unload_symbols(void) +{ + if (dll == NULL) + return; + + FreeLibrary(dll); + dll = NULL; +} + +#else /* __MINGW32_VERSION */ + +#define mingw_load_symbols() 0 +#define mingw_unload_symbols() do { } while (0) + +#endif /* __MINGW32_VERSION */ + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid, + size_t aidlen); +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len); +static int scard_verify_pin(struct scard_data *scard, const char *pin); +static int scard_get_record_len(struct scard_data *scard, + unsigned char recnum, unsigned char mode); +static int scard_read_record(struct scard_data *scard, + unsigned char *data, size_t len, + unsigned char recnum, unsigned char mode); + + +static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, + int *ps_do, int *file_len) +{ + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + wpa_printf(MSG_DEBUG, "SCARD: file header did not " + "start with FSP template tag"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if ((pos + pos[0]) < end) + end = pos + 1 + pos[0]; + pos++; + wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", + pos, end - pos); + + while (pos + 1 < end) { + wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV " + "0x%02x len=%d", pos[0], pos[1]); + if (pos + 2 + pos[1] > end) + break; + + if (pos[0] == USIM_TLV_FILE_SIZE && + (pos[1] == 1 || pos[1] == 2) && file_len) { + if (pos[1] == 1) + *file_len = (int) pos[2]; + else + *file_len = ((int) pos[2] << 8) | + (int) pos[3]; + wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", + *file_len); + } + + if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE && + pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && + pos[3] >= 1 && ps_do) { + wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", + pos[4]); + *ps_do = (int) pos[4]; + } + + pos += 2 + pos[1]; + + if (pos == end) + return 0; + } + return -1; +} + + +static int scard_pin_needed(struct scard_data *scard, + unsigned char *hdr, size_t hlen) +{ + if (scard->sim_type == SCARD_GSM_SIM) { + if (hlen > SCARD_CHV1_OFFSET && + !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) + return 1; + return 0; + } + + if (scard->sim_type == SCARD_USIM) { + int ps_do; + if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) + return -1; + /* TODO: there could be more than one PS_DO entry because of + * multiple PINs in key reference.. */ + if (ps_do > 0 && (ps_do & 0x80)) + return 1; + return 0; + } + + return -1; +} + + +static int scard_get_aid(struct scard_data *scard, unsigned char *aid, + size_t maxlen) +{ + int rlen, rec; + struct efdir { + unsigned char appl_template_tag; /* 0x61 */ + unsigned char appl_template_len; + unsigned char appl_id_tag; /* 0x4f */ + unsigned char aid_len; + unsigned char rid[5]; + unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ + } *efdir; + unsigned char buf[100]; + size_t blen; + + efdir = (struct efdir *) buf; + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); + + for (rec = 1; rec < 10; rec++) { + rlen = scard_get_record_len(scard, rec, + SIM_RECORD_MODE_ABSOLUTE); + if (rlen < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " + "record length"); + return -1; + } + blen = sizeof(buf); + if (rlen > (int) blen) { + wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); + return -1; + } + if (scard_read_record(scard, buf, rlen, rec, + SIM_RECORD_MODE_ABSOLUTE) < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read " + "EF_DIR record %d", rec); + return -1; + } + wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); + + if (efdir->appl_template_tag != 0x61) { + wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " + "template tag 0x%x", + efdir->appl_template_tag); + continue; + } + + if (efdir->appl_template_len > rlen - 2) { + wpa_printf(MSG_DEBUG, "SCARD: Too long application " + "template (len=%d rlen=%d)", + efdir->appl_template_len, rlen); + continue; + } + + if (efdir->appl_id_tag != 0x4f) { + wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " + "identifier tag 0x%x", efdir->appl_id_tag); + continue; + } + + if (efdir->aid_len < 1 || efdir->aid_len > 16) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", + efdir->aid_len); + continue; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", + efdir->rid, efdir->aid_len); + + if (efdir->appl_code[0] == 0x10 && + efdir->appl_code[1] == 0x02) { + wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " + "EF_DIR record %d", rec); + break; + } + } + + if (rec >= 10) { + wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " + "from EF_DIR records"); + return -1; + } + + if (efdir->aid_len > maxlen) { + wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); + return -1; + } + + os_memcpy(aid, efdir->rid, efdir->aid_len); + + return efdir->aid_len; +} + + +/** + * scard_init - Initialize SIM/USIM connection using PC/SC + * @sim_type: Allowed SIM types (SIM, USIM, or both) + * Returns: Pointer to private data structure, or %NULL on failure + * + * This function is used to initialize SIM/USIM connection. PC/SC is used to + * open connection to the SIM/USIM card and the card is verified to support the + * selected sim_type. In addition, local flag is set if a PIN is needed to + * access some of the card functions. Once the connection is not needed + * anymore, scard_deinit() can be used to close it. + */ +struct scard_data * scard_init(scard_sim_type sim_type) +{ + long ret; + unsigned long len; + struct scard_data *scard; +#ifdef CONFIG_NATIVE_WINDOWS + TCHAR *readers = NULL; +#else /* CONFIG_NATIVE_WINDOWS */ + char *readers = NULL; +#endif /* CONFIG_NATIVE_WINDOWS */ + unsigned char buf[100]; + size_t blen; + int transaction = 0; + int pin_needed; + + wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); + if (mingw_load_symbols()) + return NULL; + scard = os_zalloc(sizeof(*scard)); + if (scard == NULL) + return NULL; + + ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, + &scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " + "context (err=%ld)", ret); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, NULL, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " + "(err=%ld)", ret); + goto failed; + } + +#ifdef UNICODE + len *= 2; +#endif /* UNICODE */ + readers = os_malloc(len); + if (readers == NULL) { + wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, readers, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " + "(err=%ld)", ret); + goto failed; + } + if (len < 3) { + wpa_printf(MSG_WARNING, "SCARD: No smart card readers " + "available."); + goto failed; + } + /* readers is a list of available reader. Last entry is terminated with + * double NUL. + * TODO: add support for selecting the reader; now just use the first + * one.. */ +#ifdef UNICODE + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); +#else /* UNICODE */ + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); +#endif /* UNICODE */ + + ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); + if (ret != SCARD_S_SUCCESS) { + if (ret == (long) SCARD_E_NO_SMARTCARD) + wpa_printf(MSG_INFO, "No smart card inserted."); + else + wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); + goto failed; + } + + os_free(readers); + readers = NULL; + + wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", + (unsigned int) scard->card, scard->protocol, + scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); + + ret = SCardBeginTransaction(scard->card); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " + "0x%x", (unsigned int) ret); + goto failed; + } + transaction = 1; + + blen = sizeof(buf); + + scard->sim_type = SCARD_GSM_SIM; + if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { + wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); + if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, + SCARD_USIM, NULL, 0)) { + wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); + if (sim_type == SCARD_USIM_ONLY) + goto failed; + wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); + scard->sim_type = SCARD_GSM_SIM; + } else { + wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); + scard->sim_type = SCARD_USIM; + } + } + + if (scard->sim_type == SCARD_GSM_SIM) { + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); + goto failed; + } + + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); + goto failed; + } + } else { + unsigned char aid[32]; + int aid_len; + + aid_len = scard_get_aid(scard, aid, sizeof(aid)); + if (aid_len < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " + "3G USIM app - try to use standard 3G RID"); + os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); + aid_len = 5; + } + wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); + + /* Select based on AID = 3G RID from EF_DIR. This is usually + * starting with A0 00 00 00 87. */ + blen = sizeof(buf); + if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, + aid, aid_len)) { + wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " + "app"); + wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", + aid, aid_len); + goto failed; + } + } + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + pin_needed = scard_pin_needed(scard, buf, blen); + if (pin_needed < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " + "is needed"); + goto failed; + } + if (pin_needed) { + scard->pin1_required = 1; + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + } + + ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " + "0x%x", (unsigned int) ret); + } + + return scard; + +failed: + if (transaction) + SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); + os_free(readers); + scard_deinit(scard); + return NULL; +} + + +/** + * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands + * @scard: Pointer to private data from scard_init() + * @pin: PIN code as an ASCII string (e.g., "1234") + * Returns: 0 on success, -1 on failure + */ +int scard_set_pin(struct scard_data *scard, const char *pin) +{ + if (scard == NULL) + return -1; + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + if (scard->pin1_required) { + if (pin == NULL) { + wpa_printf(MSG_DEBUG, "No PIN configured for SIM " + "access"); + return -1; + } + if (scard_verify_pin(scard, pin)) { + wpa_printf(MSG_INFO, "PIN verification failed for " + "SIM access"); + return -1; + } + } + + return 0; +} + + +/** + * scard_deinit - Deinitialize SIM/USIM connection + * @scard: Pointer to private data from scard_init() + * + * This function closes the SIM/USIM connect opened with scard_init(). + */ +void scard_deinit(struct scard_data *scard) +{ + long ret; + + if (scard == NULL) + return; + + wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); + if (scard->card) { + ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " + "smart card (err=%ld)", ret); + } + } + + if (scard->ctx) { + ret = SCardReleaseContext(scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "Failed to release smart card " + "context (err=%ld)", ret); + } + } + os_free(scard); + mingw_unload_symbols(); +} + + +static long scard_transmit(struct scard_data *scard, + unsigned char *_send, size_t send_len, + unsigned char *_recv, size_t *recv_len) +{ + long ret; + unsigned long rlen; + + wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", + _send, send_len); + rlen = *recv_len; + ret = SCardTransmit(scard->card, + scard->protocol == SCARD_PROTOCOL_T1 ? + SCARD_PCI_T1 : SCARD_PCI_T0, + _send, (unsigned long) send_len, + NULL, _recv, &rlen); + *recv_len = rlen; + if (ret == SCARD_S_SUCCESS) { + wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", + _recv, rlen); + } else { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + } + return ret; +} + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid, + size_t aidlen) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[50] = { SIM_CMD_SELECT }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + size_t len, rlen; + + if (sim_type == SCARD_USIM) { + cmd[0] = USIM_CLA; + cmd[3] = 0x04; + get_resp[0] = USIM_CLA; + } + + wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); + if (aid) { + wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", + aid, aidlen); + if (5 + aidlen > sizeof(cmd)) + return -1; + cmd[2] = 0x04; /* Select by AID */ + cmd[4] = aidlen; /* len */ + os_memcpy(cmd + 5, aid, aidlen); + cmdlen = 5 + aidlen; + } else { + cmd[5] = file_id >> 8; + cmd[6] = file_id & 0xff; + cmdlen = 7; + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + return -1; + } + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " + "%d (expected 2)", (int) len); + return -1; + } + + if (resp[0] == 0x98 && resp[1] == 0x04) { + /* Security status not satisfied (PIN_WLAN) */ + wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " + "(PIN_WLAN)"); + return -1; + } + + if (resp[0] == 0x6e) { + wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); + return -1; + } + + if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " + "(expected 0x61, 0x6c, or 0x9f)", resp[0]); + return -1; + } + /* Normal ending of command; resp[1] bytes available */ + get_resp[4] = resp[1]; + wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", + resp[1]); + + rlen = *buf_len; + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); + if (ret == SCARD_S_SUCCESS) { + *buf_len = resp[1] < rlen ? resp[1] : rlen; + return 0; + } + + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); + return -1; +} + + +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len) +{ + return _scard_select_file(scard, file_id, buf, buf_len, + scard->sim_type, NULL, 0); +} + + +static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, + unsigned char mode) +{ + unsigned char buf[255]; + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen; + long ret; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = sizeof(buf); + + blen = sizeof(buf); + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " + "length for record %d", recnum); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", + buf, blen); + + if (blen < 2 || buf[0] != 0x6c) { + wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " + "length determination"); + return -1; + } + + return buf[1]; +} + + +static int scard_read_record(struct scard_data *scard, + unsigned char *data, size_t len, + unsigned char recnum, unsigned char mode) +{ + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = len; + + buf = os_malloc(blen); + if (buf == NULL) + return -1; + + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + os_free(buf); + return -2; + } + if (blen != len + 2) { + wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " + "length %ld (expected %ld)", + (long) blen, (long) len + 2); + os_free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " + "status %02x %02x (expected 90 00)", + buf[len], buf[len + 1]); + os_free(buf); + return -4; + } + + os_memcpy(data, buf, len); + os_free(buf); + + return 0; +} + + +static int scard_read_file(struct scard_data *scard, + unsigned char *data, size_t len) +{ + unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + cmd[4] = len; + + buf = os_malloc(blen); + if (buf == NULL) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + os_free(buf); + return -2; + } + if (blen != len + 2) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "length %ld (expected %ld)", + (long) blen, (long) len + 2); + os_free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "status %02x %02x (expected 90 00)", + buf[len], buf[len + 1]); + os_free(buf); + return -4; + } + + os_memcpy(data, buf, len); + os_free(buf); + + return 0; +} + + +static int scard_verify_pin(struct scard_data *scard, const char *pin) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + + wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); + + if (pin == NULL || os_strlen(pin) > 8) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + os_memcpy(cmd + 5, pin, os_strlen(pin)); + os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { + wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); + return 0; +} + + +/** + * scard_get_imsi - Read IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * @imsi: Buffer for IMSI + * @len: Length of imsi buffer; set to IMSI length on success + * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file + * selection returns invalid result code, -3 if parsing FSP template file fails + * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set + * to needed length), -5 if reading IMSI file fails. + * + * This function can be used to read IMSI from the SIM/USIM card. If the IMSI + * file is PIN protected, scard_set_pin() must have been used to set the + * correct PIN code before calling scard_get_imsi(). + */ +int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) +{ + unsigned char buf[100]; + size_t blen, imsilen, i; + char *pos; + + wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " + "header (len=%ld)", (long) blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + blen = (buf[2] << 8) | buf[3]; + } else { + int file_size; + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + blen = file_size; + } + if (blen < 2 || blen > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", + (long) blen); + return -3; + } + + imsilen = (blen - 2) * 2 + 1; + wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", + (long) blen, (long) imsilen); + if (blen < 2 || imsilen > *len) { + *len = imsilen; + return -4; + } + + if (scard_read_file(scard, buf, blen)) + return -5; + + pos = imsi; + *pos++ = '0' + (buf[1] >> 4 & 0x0f); + for (i = 2; i < blen; i++) { + unsigned char digit; + + digit = buf[i] & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + + digit = buf[i] >> 4 & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + } + *len = imsilen; + + return 0; +} + + +/** + * scard_gsm_auth - Run GSM authentication command on SIM card + * @scard: Pointer to private data from scard_init() + * @_rand: 16-byte RAND value from HLR/AuC + * @sres: 4-byte buffer for SRES + * @kc: 8-byte buffer for Kc + * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, + * -2 if authentication command execution fails, -3 if unknown response code + * for authentication command is received, -4 if reading of response fails, + * -5 if if response data is of unexpected length + * + * This function performs GSM authentication using SIM/USIM card and the + * provided RAND value from HLR/AuC. If authentication command can be completed + * successfully, SRES and Kc values will be written into sres and kc buffers. + */ +int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, + unsigned char *sres, unsigned char *kc) +{ + unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[12 + 3 + 2]; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); + if (scard->sim_type == SCARD_GSM_SIM) { + cmdlen = 5 + 16; + os_memcpy(cmd + 5, _rand, 16); + } else { + cmdlen = 5 + 1 + 16; + cmd[0] = USIM_CLA; + cmd[3] = 0x80; + cmd[4] = 17; + cmd[5] = 16; + os_memcpy(cmd + 6, _rand, 16); + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if ((scard->sim_type == SCARD_GSM_SIM && + (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || + (scard->sim_type == SCARD_USIM && + (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " + "auth request (len=%ld resp=%02x %02x)", + (long) len, resp[0], resp[1]); + return -3; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS) + return -4; + + if (scard->sim_type == SCARD_GSM_SIM) { + if (len != 4 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for GSM auth (len=%ld, expected 14)", + (long) len); + return -5; + } + os_memcpy(sres, buf, 4); + os_memcpy(kc, buf + 4, 8); + } else { + if (len != 1 + 4 + 1 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for USIM auth (len=%ld, " + "expected 16)", (long) len); + return -5; + } + if (buf[0] != 4 || buf[5] != 8) { + wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " + "length (%d %d, expected 4 8)", + buf[0], buf[5]); + } + os_memcpy(sres, buf + 1, 4); + os_memcpy(kc, buf + 6, 8); + } + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); + + return 0; +} + + +/** + * scard_umts_auth - Run UMTS authentication command on USIM card + * @scard: Pointer to private data from scard_init() + * @_rand: 16-byte RAND value from HLR/AuC + * @autn: 16-byte AUTN value from HLR/AuC + * @res: 16-byte buffer for RES + * @res_len: Variable that will be set to RES length + * @ik: 16-byte buffer for IK + * @ck: 16-byte buffer for CK + * @auts: 14-byte buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization + * failure + * + * This function performs AKA authentication using USIM card and the provided + * RAND and AUTN values from HLR/AuC. If authentication command can be + * completed successfully, RES, IK, and CK values will be written into provided + * buffers and res_len is set to length of received RES value. If USIM reports + * synchronization failure, the received AUTS value will be written into auts + * buffer. In this case, RES, IK, and CK are not valid. + */ +int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts) +{ + unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = + { USIM_CMD_RUN_UMTS_ALG }; + unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[64], *pos, *end; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + if (scard->sim_type == SCARD_GSM_SIM) { + wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " + "auth"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); + cmd[5] = AKA_RAND_LEN; + os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); + cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; + os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -1; + + if (len <= sizeof(resp)) + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); + + if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { + wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " + "MAC != XMAC"); + return -1; + } else if (len != 2 || resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " + "auth request (len=%ld resp=%02x %02x)", + (long) len, resp[0], resp[1]); + return -1; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); + if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && + buf[1] == AKA_AUTS_LEN) { + wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); + os_memcpy(auts, buf + 2, AKA_AUTS_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); + return -2; + } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { + pos = buf + 1; + end = buf + len; + + /* RES */ + if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); + return -1; + } + *res_len = *pos++; + os_memcpy(res, pos, *res_len); + pos += *res_len; + wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); + + /* CK */ + if (pos[0] != CK_LEN || pos + CK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); + return -1; + } + pos++; + os_memcpy(ck, pos, CK_LEN); + pos += CK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); + + /* IK */ + if (pos[0] != IK_LEN || pos + IK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); + return -1; + } + pos++; + os_memcpy(ik, pos, IK_LEN); + pos += IK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); + + return 0; + } + + wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); + return -1; +} diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h new file mode 100644 index 0000000000000..543f7c598419a --- /dev/null +++ b/src/utils/pcsc_funcs.h @@ -0,0 +1,68 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PCSC_FUNCS_H +#define PCSC_FUNCS_H + +/* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ +#define SCARD_FILE_MF 0x3F00 +#define SCARD_FILE_GSM_DF 0x7F20 +#define SCARD_FILE_UMTS_DF 0x7F50 +#define SCARD_FILE_GSM_EF_IMSI 0x6F07 +#define SCARD_FILE_EF_DIR 0x2F00 +#define SCARD_FILE_EF_ICCID 0x2FE2 +#define SCARD_FILE_EF_CK 0x6FE1 +#define SCARD_FILE_EF_IK 0x6FE2 + +#define SCARD_CHV1_OFFSET 13 +#define SCARD_CHV1_FLAG 0x80 + +typedef enum { + SCARD_GSM_SIM_ONLY, + SCARD_USIM_ONLY, + SCARD_TRY_BOTH +} scard_sim_type; + + +#ifdef PCSC_FUNCS +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, const unsigned char *_rand, + unsigned char *sres, unsigned char *kc); +int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts); + +#else /* PCSC_FUNCS */ + +#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 + +#endif /* PCSC_FUNCS */ + +#endif /* PCSC_FUNCS_H */ diff --git a/contrib/hostapd/state_machine.h b/src/utils/state_machine.h index 62766bf40ba4e..31f667217f1ac 100644 --- a/contrib/hostapd/state_machine.h +++ b/src/utils/state_machine.h @@ -14,7 +14,7 @@ * This file includes a set of pre-processor macros that can be used to * implement a state machine. In addition to including this header file, each * file implementing a state machine must define STATE_MACHINE_DATA to be the - * data structure including state variables (enum <machine>_state, + * data structure including state variables (enum machine_state, * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define * a group of state machines with shared data structure, STATE_MACHINE_ADDR @@ -61,7 +61,7 @@ sm->machine ## _state = machine ## _ ## state; * SM_ENTRY_M - State machine function entry point for state machine group * @machine: State machine name * @_state: State machine state - * @data: State variable prefix (full variable: <prefix>_state) + * @data: State variable prefix (full variable: prefix_state) * * This macro is like SM_ENTRY, but for state machine groups that use a shared * data structure for more than one state machine. Both machine and prefix @@ -80,7 +80,7 @@ sm->data ## _ ## state = machine ## _ ## _state; * SM_ENTRY_MA - State machine function entry point for state machine group * @machine: State machine name * @_state: State machine state - * @data: State variable prefix (full variable: <prefix>_state) + * @data: State variable prefix (full variable: prefix_state) * * This macro is like SM_ENTRY_M, but a MAC address is included in debug * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to diff --git a/src/utils/uuid.c b/src/utils/uuid.c new file mode 100644 index 0000000000000..620d3d610cd87 --- /dev/null +++ b/src/utils/uuid.c @@ -0,0 +1,107 @@ +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "sha1.h" +#include "uuid.h" + +int uuid_str2bin(const char *str, u8 *bin) +{ + const char *pos; + u8 *opos; + + pos = str; + opos = bin; + + if (hexstr2bin(pos, opos, 4)) + return -1; + pos += 8; + opos += 4; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 6)) + return -1; + + return 0; +} + + +int uuid_bin2str(const u8 *bin, char *str, size_t max_len) +{ + int len; + len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + bin[0], bin[1], bin[2], bin[3], + bin[4], bin[5], bin[6], bin[7], + bin[8], bin[9], bin[10], bin[11], + bin[12], bin[13], bin[14], bin[15]); + if (len < 0 || (size_t) len >= max_len) + return -1; + return 0; +} + + +int is_nil_uuid(const u8 *uuid) +{ + int i; + for (i = 0; i < UUID_LEN; i++) + if (uuid[i]) + return 0; + return 1; +} + + +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid) +{ + const u8 *addr[2]; + size_t len[2]; + u8 hash[SHA1_MAC_LEN]; + u8 nsid[16] = { + 0x52, 0x64, 0x80, 0xf8, + 0xc9, 0x9b, + 0x4b, 0xe5, + 0xa6, 0x55, + 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84 + }; + + addr[0] = nsid; + len[0] = sizeof(nsid); + addr[1] = mac_addr; + len[1] = 6; + sha1_vector(2, addr, len, hash); + os_memcpy(uuid, hash, 16); + + /* Version: 5 = named-based version using SHA-1 */ + uuid[6] = (5 << 4) | (uuid[6] & 0x0f); + + /* Variant specified in RFC 4122 */ + uuid[8] = 0x80 | (uuid[8] & 0x3f); +} diff --git a/src/utils/uuid.h b/src/utils/uuid.h new file mode 100644 index 0000000000000..9fc2ba06822fa --- /dev/null +++ b/src/utils/uuid.h @@ -0,0 +1,25 @@ +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef UUID_H +#define UUID_H + +#define UUID_LEN 16 + +int uuid_str2bin(const char *str, u8 *bin); +int uuid_bin2str(const u8 *bin, char *str, size_t max_len); +int is_nil_uuid(const u8 *uuid); +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); + +#endif /* UUID_H */ diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c new file mode 100644 index 0000000000000..e17cf06ef2e76 --- /dev/null +++ b/src/utils/wpa_debug.c @@ -0,0 +1,326 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" + + +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ +int wpa_debug_level = MSG_INFO; +int wpa_debug_show_keys = 0; +int wpa_debug_timestamp = 0; + + +#ifndef CONFIG_NO_STDOUT_DEBUG + +void wpa_debug_print_timestamp(void) +{ + struct os_time tv; + + if (!wpa_debug_timestamp) + return; + + os_get_time(&tv); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + fprintf(out_file, "%ld.%06u: ", (long) tv.sec, + (unsigned int) tv.usec); + } else +#endif /* CONFIG_DEBUG_FILE */ + printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +} + + +/** + * 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; + + va_start(ap, fmt); + if (level >= wpa_debug_level) { + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + vfprintf(out_file, fmt, ap); + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + vprintf(fmt, ap); + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ + } + va_end(ap); +} + + +static void _wpa_hexdump(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i; + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + fprintf(out_file, "%s - hexdump(len=%lu):", + title, (unsigned long) len); + if (buf == NULL) { + fprintf(out_file, " [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + fprintf(out_file, " %02x", buf[i]); + } else { + fprintf(out_file, " [REMOVED]"); + } + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + if (buf == NULL) { + printf(" [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + } else { + printf(" [REMOVED]"); + } + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +} + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, 1); +} + + +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); +} + + +static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i, llen; + const u8 *pos = buf; + const size_t line_len = 16; + + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + if (!show) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n", + title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + fprintf(out_file, " "); + for (i = 0; i < llen; i++) + fprintf(out_file, " %02x", pos[i]); + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, " "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + fprintf(out_file, "%c", pos[i]); + else + fprintf(out_file, "_"); + } + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, "\n"); + pos += llen; + len -= llen; + } + } else { +#endif /* CONFIG_DEBUG_FILE */ + if (!show) { + printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + printf(" "); + for (i = 0; i < llen; i++) + printf(" %02x", pos[i]); + for (i = llen; i < line_len; i++) + printf(" "); + printf(" "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + printf("%c", pos[i]); + else + printf("_"); + } + for (i = llen; i < line_len; i++) + printf(" "); + printf("\n"); + pos += llen; + len -= llen; + } +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +} + + +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, 1); +} + + +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); +} + + +int wpa_debug_open_file(const char *path) +{ +#ifdef CONFIG_DEBUG_FILE + if (!path) + return 0; + out_file = fopen(path, "a"); + if (out_file == NULL) { + wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " + "output file, using standard output"); + return -1; + } +#ifndef _WIN32 + setvbuf(out_file, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +#endif /* CONFIG_DEBUG_FILE */ + return 0; +} + + +void wpa_debug_close_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + if (!out_file) + return; + fclose(out_file); + out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifndef CONFIG_NO_WPA_MSG +static wpa_msg_cb_func wpa_msg_cb = NULL; + +void wpa_msg_register_cb(wpa_msg_cb_func func) +{ + wpa_msg_cb = func; +} + + +void wpa_msg(void *ctx, int level, char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message " + "buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, buf, len); + os_free(buf); +} +#endif /* CONFIG_NO_WPA_MSG */ + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static hostapd_logger_cb_func hostapd_logger_cb = NULL; + +void hostapd_logger_register_cb(hostapd_logger_cb_func func) +{ + hostapd_logger_cb = func; +} + + +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, + const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + if (hostapd_logger_cb) + hostapd_logger_cb(ctx, addr, module, level, buf, len); + else + wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); + os_free(buf); +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h new file mode 100644 index 0000000000000..2d23f06497a59 --- /dev/null +++ b/src/utils/wpa_debug.h @@ -0,0 +1,223 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_DEBUG_H +#define WPA_DEBUG_H + +#include "wpabuf.h" + +/* Debugging function - conditional printf and hex dump. Driver wrappers can + * use these for debugging purposes. */ + +enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; + +#ifdef CONFIG_NO_STDOUT_DEBUG + +#define wpa_debug_print_timestamp() do { } while (0) +#define wpa_printf(args...) do { } while (0) +#define wpa_hexdump(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf(l,t,b) do { } while (0) +#define wpa_hexdump_key(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf_key(l,t,b) do { } while (0) +#define wpa_hexdump_ascii(l,t,b,le) do { } while (0) +#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) +#define wpa_debug_open_file(p) do { } while (0) +#define wpa_debug_close_file() do { } while (0) + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +int wpa_debug_open_file(const char *path); +void wpa_debug_close_file(void); + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in seconds_from_1970.microsoconds + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, char *fmt, ...) +PRINTF_FORMAT(2, 3); + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. + */ +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len); + +/** + * wpa_hexdump_ascii_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by + * default, does not include secret keys (passwords, etc.) in debug output. + */ +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len); + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NO_WPA_MSG +#define wpa_msg(args...) do { } while (0) +#define wpa_msg_register_cb(f) do { } while (0) +#else /* CONFIG_NO_WPA_MSG */ +/** + * wpa_msg - Conditional printf for default target and ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(void *ctx, int level, char *fmt, ...) PRINTF_FORMAT(3, 4); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, + size_t len); + +/** + * wpa_msg_register_cb - Register callback function for wpa_msg() messages + * @func: Callback function (%NULL to unregister) + */ +void wpa_msg_register_cb(wpa_msg_cb_func func); +#endif /* CONFIG_NO_WPA_MSG */ + + +#ifdef CONFIG_NO_HOSTAPD_LOGGER +#define hostapd_logger(args...) do { } while (0) +#define hostapd_logger_register_cb(f) do { } while (0) +#else /* CONFIG_NO_HOSTAPD_LOGGER */ +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, + const char *fmt, ...) PRINTF_FORMAT(5, 6); + +typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr, + unsigned int module, int level, + const char *txt, size_t len); + +/** + * hostapd_logger_register_cb - Register callback function for hostapd_logger() + * @func: Callback function (%NULL to unregister) + */ +void hostapd_logger_register_cb(hostapd_logger_cb_func func); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + +#define HOSTAPD_MODULE_IEEE80211 0x00000001 +#define HOSTAPD_MODULE_IEEE8021X 0x00000002 +#define HOSTAPD_MODULE_RADIUS 0x00000004 +#define HOSTAPD_MODULE_WPA 0x00000008 +#define HOSTAPD_MODULE_DRIVER 0x00000010 +#define HOSTAPD_MODULE_IAPP 0x00000020 +#define HOSTAPD_MODULE_MLME 0x00000040 + +enum hostapd_logger_level { + HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, + HOSTAPD_LEVEL_DEBUG = 1, + HOSTAPD_LEVEL_INFO = 2, + HOSTAPD_LEVEL_NOTICE = 3, + HOSTAPD_LEVEL_WARNING = 4 +}; + + + +#ifdef EAPOL_TEST +#define WPA_ASSERT(a) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +#endif /* WPA_DEBUG_H */ diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c new file mode 100644 index 0000000000000..c5441790e0bac --- /dev/null +++ b/src/utils/wpabuf.c @@ -0,0 +1,212 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpabuf.h" + +static void wpabuf_overflow(const struct wpabuf *buf, size_t len) +{ + wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", + buf, (unsigned long) buf->size, (unsigned long) buf->used, + (unsigned long) len); + abort(); +} + + +int wpabuf_resize(struct wpabuf **_buf, size_t add_len) +{ + struct wpabuf *buf = *_buf; + if (buf->used + add_len > buf->size) { + unsigned char *nbuf; + if (buf->ext_data) { + nbuf = os_realloc(buf->ext_data, buf->used + add_len); + if (nbuf == NULL) + return -1; + os_memset(nbuf + buf->used, 0, add_len); + buf->ext_data = nbuf; + } else { + nbuf = os_realloc(buf, sizeof(struct wpabuf) + + buf->used + add_len); + if (nbuf == NULL) + return -1; + buf = (struct wpabuf *) nbuf; + os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, + add_len); + *_buf = buf; + } + buf->size = buf->used + add_len; + } + + return 0; +} + + +/** + * wpabuf_alloc - Allocate a wpabuf of the given size + * @len: Length for the allocated buffer + * Returns: Buffer to the allocated wpabuf or %NULL on failure + */ +struct wpabuf * wpabuf_alloc(size_t len) +{ + struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len); + if (buf == NULL) + return NULL; + buf->size = len; + return buf; +} + + +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) +{ + struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf)); + if (buf == NULL) + return NULL; + + buf->size = len; + buf->used = len; + buf->ext_data = data; + + return buf; +} + + +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len) +{ + struct wpabuf *buf = wpabuf_alloc(len); + if (buf) + wpabuf_put_data(buf, data, len); + return buf; +} + + +struct wpabuf * wpabuf_dup(const struct wpabuf *src) +{ + struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src)); + if (buf) + wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); + return buf; +} + + +/** + * wpabuf_free - Free a wpabuf + * @buf: wpabuf buffer + */ +void wpabuf_free(struct wpabuf *buf) +{ + if (buf == NULL) + return; + os_free(buf->ext_data); + os_free(buf); +} + + +void * wpabuf_put(struct wpabuf *buf, size_t len) +{ + void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + buf->used += len; + if (buf->used > buf->size) { + wpabuf_overflow(buf, len); + } + return tmp; +} + + +/** + * wpabuf_concat - Concatenate two buffers into a newly allocated one + * @a: First buffer + * @b: Second buffer + * Returns: wpabuf with concatenated a + b data or %NULL on failure + * + * Both buffers a and b will be freed regardless of the return value. Input + * buffers can be %NULL which is interpreted as an empty buffer. + */ +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b) +{ + struct wpabuf *n = NULL; + size_t len = 0; + + if (b == NULL) + return a; + + if (a) + len += wpabuf_len(a); + if (b) + len += wpabuf_len(b); + + n = wpabuf_alloc(len); + if (n) { + if (a) + wpabuf_put_buf(n, a); + if (b) + wpabuf_put_buf(n, b); + } + + wpabuf_free(a); + wpabuf_free(b); + + return n; +} + + +/** + * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length + * @buf: Buffer to be padded + * @len: Length for the padded buffer + * Returns: wpabuf padded to len octets or %NULL on failure + * + * If buf is longer than len octets or of same size, it will be returned as-is. + * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed + * by the source data. The source buffer will be freed on error, i.e., caller + * will only be responsible on freeing the returned buffer. If buf is %NULL, + * %NULL will be returned. + */ +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len) +{ + struct wpabuf *ret; + size_t blen; + + if (buf == NULL) + return NULL; + + blen = wpabuf_len(buf); + if (blen >= len) + return buf; + + ret = wpabuf_alloc(len); + if (ret) { + os_memset(wpabuf_put(ret, len - blen), 0, len - blen); + wpabuf_put_buf(ret, buf); + } + wpabuf_free(buf); + + return ret; +} + + +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) +{ + va_list ap; + void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + int res; + + va_start(ap, fmt); + res = vsnprintf(tmp, buf->size - buf->used, fmt, ap); + va_end(ap); + if (res < 0 || (size_t) res >= buf->size - buf->used) + wpabuf_overflow(buf, res); + buf->used += res; +} diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h new file mode 100644 index 0000000000000..bd8f09e94f840 --- /dev/null +++ b/src/utils/wpabuf.h @@ -0,0 +1,156 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPABUF_H +#define WPABUF_H + +/* + * Internal data structure for wpabuf. Please do not touch this directly from + * elsewhere. This is only defined in header file to allow inline functions + * from this file to access data. + */ +struct wpabuf { + size_t size; /* total size of the allocated buffer */ + size_t used; /* length of data in the buffer */ + u8 *ext_data; /* pointer to external data; NULL if data follows + * struct wpabuf */ + /* optionally followed by the allocated buffer */ +}; + + +int wpabuf_resize(struct wpabuf **buf, size_t add_len); +struct wpabuf * wpabuf_alloc(size_t len); +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); +struct wpabuf * wpabuf_dup(const struct wpabuf *src); +void wpabuf_free(struct wpabuf *buf); +void * wpabuf_put(struct wpabuf *buf, size_t len); +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); + + +/** + * wpabuf_size - Get the currently allocated size of a wpabuf buffer + * @buf: wpabuf buffer + * Returns: Currently allocated size of the buffer + */ +static inline size_t wpabuf_size(const struct wpabuf *buf) +{ + return buf->size; +} + +/** + * wpabuf_len - Get the current length of a wpabuf buffer data + * @buf: wpabuf buffer + * Returns: Currently used length of the buffer + */ +static inline size_t wpabuf_len(const struct wpabuf *buf) +{ + return buf->used; +} + +/** + * wpabuf_tailroom - Get size of available tail room in the end of the buffer + * @buf: wpabuf buffer + * Returns: Tail room (in bytes) of available space in the end of the buffer + */ +static inline size_t wpabuf_tailroom(const struct wpabuf *buf) +{ + return buf->size - buf->used; +} + +/** + * wpabuf_head - Get pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline const void * wpabuf_head(const struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) +{ + return wpabuf_head(buf); +} + +/** + * wpabuf_mhead - Get modifiable pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline void * wpabuf_mhead(struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) +{ + return wpabuf_mhead(buf); +} + +static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) +{ + u8 *pos = wpabuf_put(buf, 1); + *pos = data; +} + +static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_BE16(pos, data); +} + +static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 3); + WPA_PUT_BE24(pos, data); +} + +static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_BE32(pos, data); +} + +static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, + size_t len) +{ + if (data) + os_memcpy(wpabuf_put(buf, len), data, len); +} + +static inline void wpabuf_put_buf(struct wpabuf *dst, + const struct wpabuf *src) +{ + wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); +} + +static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) +{ + buf->ext_data = (u8 *) data; + buf->size = buf->used = len; +} + +static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) +{ + wpabuf_put_data(dst, str, os_strlen(str)); +} + +#endif /* WPABUF_H */ diff --git a/src/wps/.gitignore b/src/wps/.gitignore new file mode 100644 index 0000000000000..a4383358ec72f --- /dev/null +++ b/src/wps/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/src/wps/Makefile b/src/wps/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/wps/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/wps/httpread.c b/src/wps/httpread.c new file mode 100644 index 0000000000000..313b4687c8f78 --- /dev/null +++ b/src/wps/httpread.c @@ -0,0 +1,858 @@ +/** + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * The files are buffered via internal callbacks from eloop, then presented to + * an application callback routine when completely read into memory. May also + * be used if no file is expected but just to get the header, including HTTP + * replies (e.g. HTTP/1.1 200 OK etc.). + * + * This does not attempt to be an optimally efficient implementation, but does + * attempt to be of reasonably small size and memory consumption; assuming that + * only small files are to be read. A maximum file size is provided by + * application and enforced. + * + * It is assumed that the application does not expect any of the following: + * -- transfer encoding other than chunked + * -- trailer fields + * It is assumed that, even if the other side requested that the connection be + * kept open, that we will close it (thus HTTP messages sent by application + * should have the connection closed field); this is allowed by HTTP/1.1 and + * simplifies things for us. + * + * Other limitations: + * -- HTTP header may not exceed a hard-coded size. + * + * Notes: + * This code would be massively simpler without some of the new features of + * HTTP/1.1, especially chunked data. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "httpread.h" + + +/* Tunable parameters */ +#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ +#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ +#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ + +#if 0 +/* httpread_debug -- set this global variable > 0 e.g. from debugger + * to enable debugs (larger numbers for more debugs) + * Make this a #define of 0 to eliminate the debugging code. + */ +int httpread_debug = 99; +#else +#define httpread_debug 0 /* eliminates even the debugging code */ +#endif + + +/* control instance -- actual definition (opaque to application) + */ +struct httpread { + /* information from creation */ + int sd; /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e); /* call on event */ + void *cookie; /* pass to callback */ + int max_bytes; /* maximum file size else abort it */ + int timeout_seconds; /* 0 or total duration timeout period */ + + /* dynamically used information follows */ + int sd_registered; /* nonzero if we need to unregister socket */ + int to_registered; /* nonzero if we need to unregister timeout */ + + int got_hdr; /* nonzero when header is finalized */ + char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ + int hdr_nbytes; + + enum httpread_hdr_type hdr_type; + int version; /* 1 if we've seen 1.1 */ + int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ + int got_content_length; /* true if we know content length for sure */ + int content_length; /* body length, iff got_content_length */ + int chunked; /* nonzero for chunked data */ + char *uri; + + int got_body; /* nonzero when body is finalized */ + char *body; + int body_nbytes; + int body_alloc_nbytes; /* amount allocated */ + + int got_file; /* here when we are done */ + + /* The following apply if data is chunked: */ + int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ + int chunk_start; /* offset in body of chunk hdr or data */ + int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ + int in_trailer; /* in header fields after data (chunked only)*/ + enum trailer_state { + trailer_line_begin = 0, + trailer_empty_cr, /* empty line + CR */ + trailer_nonempty, + trailer_nonempty_cr, + } trailer_state; +}; + + +/* Check words for equality, where words consist of graphical characters + * delimited by whitespace + * Returns nonzero if "equal" doing case insensitive comparison. + */ +static int word_eq(char *s1, char *s2) +{ + int c1; + int c2; + int end1 = 0; + int end2 = 0; + for (;;) { + c1 = *s1++; + c2 = *s2++; + if (isalpha(c1) && isupper(c1)) + c1 = tolower(c1); + if (isalpha(c2) && isupper(c2)) + c2 = tolower(c2); + end1 = !isgraph(c1); + end2 = !isgraph(c2); + if (end1 || end2 || c1 != c2) + break; + } + return end1 && end2; /* reached end of both words? */ +} + + +/* convert hex to binary + * Requires that c have been previously tested true with isxdigit(). + */ +static int hex_value(int c) +{ + if (isdigit(c)) + return c - '0'; + if (islower(c)) + return 10 + c - 'a'; + return 10 + c - 'A'; +} + + +static void httpread_timeout_handler(void *eloop_data, void *user_ctx); + +/* httpread_destroy -- if h is non-NULL, clean up + * This must eventually be called by the application following + * call of the application's callback and may be called + * earlier if desired. + */ +void httpread_destroy(struct httpread *h) +{ + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); + if (!h) + return; + + if (h->to_registered) + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + h->to_registered = 0; + if (h->sd_registered) + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + h->sd_registered = 0; + os_free(h->body); + os_free(h->uri); + os_memset(h, 0, sizeof(*h)); /* aid debugging */ + h->sd = -1; /* aid debugging */ + os_free(h); +} + + +/* httpread_timeout_handler -- called on excessive total duration + */ +static void httpread_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct httpread *h = user_ctx; + wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); + h->to_registered = 0; /* is self-cancelling */ + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); +} + + +/* Analyze options only so far as is needed to correctly obtain the file. + * The application can look at the raw header to find other options. + */ +static int httpread_hdr_option_analyze( + struct httpread *h, + char *hbp /* pointer to current line in header buffer */ + ) +{ + if (word_eq(hbp, "CONTENT-LENGTH:")) { + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + if (!isdigit(*hbp)) + return -1; + h->content_length = atol(hbp); + h->got_content_length = 1; + return 0; + } + if (word_eq(hbp, "TRANSFER_ENCODING:")) { + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* There should (?) be no encodings of interest + * other than chunked... + */ + if (os_strncmp(hbp, "CHUNKED", 7)) { + h->chunked = 1; + h->in_chunk_data = 0; + /* ignore possible ;<parameters> */ + } + return 0; + } + /* skip anything we don't know, which is a lot */ + return 0; +} + + +static int httpread_hdr_analyze(struct httpread *h) +{ + char *hbp = h->hdr; /* pointer into h->hdr */ + int standard_first_line = 1; + + /* First line is special */ + h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; + if (!isgraph(*hbp)) + goto bad; + if (os_strncmp(hbp, "HTTP/", 5) == 0) { + h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; + standard_first_line = 0; + hbp += 5; + if (hbp[0] == '1' && hbp[1] == '.' && + isdigit(hbp[2]) && hbp[2] != '0') + h->version = 1; + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + if (!isdigit(*hbp)) + goto bad; + h->reply_code = atol(hbp); + } else if (word_eq(hbp, "GET")) + h->hdr_type = HTTPREAD_HDR_TYPE_GET; + else if (word_eq(hbp, "HEAD")) + h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; + else if (word_eq(hbp, "POST")) + h->hdr_type = HTTPREAD_HDR_TYPE_POST; + else if (word_eq(hbp, "PUT")) + h->hdr_type = HTTPREAD_HDR_TYPE_PUT; + else if (word_eq(hbp, "DELETE")) + h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; + else if (word_eq(hbp, "TRACE")) + h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; + else if (word_eq(hbp, "CONNECT")) + h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; + else if (word_eq(hbp, "NOTIFY")) + h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; + else if (word_eq(hbp, "M-SEARCH")) + h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; + else if (word_eq(hbp, "M-POST")) + h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; + else if (word_eq(hbp, "SUBSCRIBE")) + h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; + else if (word_eq(hbp, "UNSUBSCRIBE")) + h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; + else { + } + + if (standard_first_line) { + char *rawuri; + char *uri; + /* skip type */ + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* parse uri. + * Find length, allocate memory for translated + * copy, then translate by changing %<hex><hex> + * into represented value. + */ + rawuri = hbp; + while (isgraph(*hbp)) + hbp++; + h->uri = os_malloc((hbp - rawuri) + 1); + if (h->uri == NULL) + goto bad; + uri = h->uri; + while (rawuri < hbp) { + int c = *rawuri; + if (c == '%' && + isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { + *uri++ = (hex_value(rawuri[1]) << 4) | + hex_value(rawuri[2]); + rawuri += 3; + } else { + *uri++ = c; + rawuri++; + } + } + *uri = 0; /* null terminate */ + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* get version */ + if (0 == strncmp(hbp, "HTTP/", 5)) { + hbp += 5; + if (hbp[0] == '1' && hbp[1] == '.' && + isdigit(hbp[2]) && hbp[2] != '0') + h->version = 1; + } + } + /* skip rest of line */ + while (*hbp) + if (*hbp++ == '\n') + break; + + /* Remainder of lines are options, in any order; + * or empty line to terminate + */ + for (;;) { + /* Empty line to terminate */ + if (hbp[0] == '\n' || + (hbp[0] == '\r' && hbp[1] == '\n')) + break; + if (!isgraph(*hbp)) + goto bad; + if (httpread_hdr_option_analyze(h, hbp)) + goto bad; + /* skip line */ + while (*hbp) + if (*hbp++ == '\n') + break; + } + + /* chunked overrides content-length always */ + if (h->chunked) + h->got_content_length = 0; + + /* For some types, we should not try to read a body + * This is in addition to the application determining + * that we should not read a body. + */ + switch (h->hdr_type) { + case HTTPREAD_HDR_TYPE_REPLY: + /* Some codes can have a body and some not. + * For now, just assume that any other than 200 + * do not... + */ + if (h->reply_code != 200) + h->max_bytes = 0; + break; + case HTTPREAD_HDR_TYPE_GET: + case HTTPREAD_HDR_TYPE_HEAD: + /* in practice it appears that it is assumed + * that GETs have a body length of 0... ? + */ + if (h->chunked == 0 && h->got_content_length == 0) + h->max_bytes = 0; + break; + case HTTPREAD_HDR_TYPE_POST: + case HTTPREAD_HDR_TYPE_PUT: + case HTTPREAD_HDR_TYPE_DELETE: + case HTTPREAD_HDR_TYPE_TRACE: + case HTTPREAD_HDR_TYPE_CONNECT: + case HTTPREAD_HDR_TYPE_NOTIFY: + case HTTPREAD_HDR_TYPE_M_SEARCH: + case HTTPREAD_HDR_TYPE_M_POST: + case HTTPREAD_HDR_TYPE_SUBSCRIBE: + case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: + default: + break; + } + + return 0; + +bad: + /* Error */ + return -1; +} + + +/* httpread_read_handler -- called when socket ready to read + * + * Note: any extra data we read past end of transmitted file is ignored; + * if we were to support keeping connections open for multiple files then + * this would have to be addressed. + */ +static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct httpread *h = sock_ctx; + int nread; + char *rbp; /* pointer into read buffer */ + char *hbp; /* pointer into header buffer */ + char *bbp; /* pointer into body buffer */ + char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ + + if (httpread_debug >= 20) + wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); + + /* read some at a time, then search for the interal + * boundaries between header and data and etc. + */ + nread = read(h->sd, readbuf, sizeof(readbuf)); + if (nread < 0) + goto bad; + if (nread == 0) { + /* end of transmission... this may be normal + * or may be an error... in some cases we can't + * tell which so we must assume it is normal then. + */ + if (!h->got_hdr) { + /* Must at least have completed header */ + wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); + goto bad; + } + if (h->chunked || h->got_content_length) { + /* Premature EOF; e.g. dropped connection */ + wpa_printf(MSG_DEBUG, + "httpread premature eof(%p) %d/%d", + h, h->body_nbytes, + h->content_length); + goto bad; + } + /* No explicit length, hopefully we have all the data + * although dropped connections can cause false + * end + */ + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); + h->got_body = 1; + goto got_file; + } + rbp = readbuf; + + /* Header consists of text lines (terminated by both CR and LF) + * and an empty line (CR LF only). + */ + if (!h->got_hdr) { + hbp = h->hdr + h->hdr_nbytes; + /* add to headers until: + * -- we run out of data in read buffer + * -- or, we run out of header buffer room + * -- or, we get double CRLF in headers + */ + for (;;) { + if (nread == 0) + goto get_more; + if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { + goto bad; + } + *hbp++ = *rbp++; + nread--; + h->hdr_nbytes++; + if (h->hdr_nbytes >= 4 && + hbp[-1] == '\n' && + hbp[-2] == '\r' && + hbp[-3] == '\n' && + hbp[-4] == '\r' ) { + h->got_hdr = 1; + *hbp = 0; /* null terminate */ + break; + } + } + /* here we've just finished reading the header */ + if (httpread_hdr_analyze(h)) { + wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); + goto bad; + } + if (h->max_bytes == 0) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread no body hdr end(%p)", h); + goto got_file; + } + if (h->got_content_length && h->content_length == 0) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread zero content length(%p)", + h); + goto got_file; + } + } + + /* Certain types of requests never have data and so + * must be specially recognized. + */ + if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || + !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || + !os_strncasecmp(h->hdr, "HEAD", 4) || + !os_strncasecmp(h->hdr, "GET", 3)) { + if (!h->got_body) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread NO BODY for sp. type"); + } + h->got_body = 1; + goto got_file; + } + + /* Data can be just plain binary data, or if "chunked" + * consists of chunks each with a header, ending with + * an ending header. + */ + if (!h->got_body) { + /* Here to get (more of) body */ + /* ensure we have enough room for worst case for body + * plus a null termination character + */ + if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { + char *new_body; + int new_alloc_nbytes; + + if (h->body_nbytes >= h->max_bytes) + goto bad; + new_alloc_nbytes = h->body_alloc_nbytes + + HTTPREAD_BODYBUF_DELTA; + /* For content-length case, the first time + * through we allocate the whole amount + * we need. + */ + if (h->got_content_length && + new_alloc_nbytes < (h->content_length + 1)) + new_alloc_nbytes = h->content_length + 1; + if ((new_body = os_realloc(h->body, new_alloc_nbytes)) + == NULL) + goto bad; + + h->body = new_body; + h->body_alloc_nbytes = new_alloc_nbytes; + } + /* add bytes */ + bbp = h->body + h->body_nbytes; + for (;;) { + int ncopy; + /* See if we need to stop */ + if (h->chunked && h->in_chunk_data == 0) { + /* in chunk header */ + char *cbp = h->body + h->chunk_start; + if (bbp-cbp >= 2 && bbp[-2] == '\r' && + bbp[-1] == '\n') { + /* end of chunk hdr line */ + /* hdr line consists solely + * of a hex numeral and CFLF + */ + if (!isxdigit(*cbp)) + goto bad; + h->chunk_size = strtoul(cbp, NULL, 16); + /* throw away chunk header + * so we have only real data + */ + h->body_nbytes = h->chunk_start; + bbp = cbp; + if (h->chunk_size == 0) { + /* end of chunking */ + /* trailer follows */ + h->in_trailer = 1; + if (httpread_debug >= 20) + wpa_printf( + MSG_DEBUG, + "httpread end chunks(%p)", h); + break; + } + h->in_chunk_data = 1; + /* leave chunk_start alone */ + } + } else if (h->chunked) { + /* in chunk data */ + if ((h->body_nbytes - h->chunk_start) == + (h->chunk_size + 2)) { + /* end of chunk reached, + * new chunk starts + */ + /* check chunk ended w/ CRLF + * which we'll throw away + */ + if (bbp[-1] == '\n' && + bbp[-2] == '\r') { + } else + goto bad; + h->body_nbytes -= 2; + bbp -= 2; + h->chunk_start = h->body_nbytes; + h->in_chunk_data = 0; + h->chunk_size = 0; /* just in case */ + } + } else if (h->got_content_length && + h->body_nbytes >= h->content_length) { + h->got_body = 1; + if (httpread_debug >= 10) + wpa_printf( + MSG_DEBUG, + "httpread got content(%p)", h); + goto got_file; + } + if (nread <= 0) + break; + /* Now transfer. Optimize using memcpy where we can. */ + if (h->chunked && h->in_chunk_data) { + /* copy up to remainder of chunk data + * plus the required CR+LF at end + */ + ncopy = (h->chunk_start + h->chunk_size + 2) - + h->body_nbytes; + } else if (h->chunked) { + /*in chunk header -- don't optimize */ + *bbp++ = *rbp++; + nread--; + h->body_nbytes++; + continue; + } else if (h->got_content_length) { + ncopy = h->content_length - h->body_nbytes; + } else { + ncopy = nread; + } + /* Note: should never be 0 */ + if (ncopy > nread) + ncopy = nread; + os_memcpy(bbp, rbp, ncopy); + bbp += ncopy; + h->body_nbytes += ncopy; + rbp += ncopy; + nread -= ncopy; + } /* body copy loop */ + } /* !got_body */ + if (h->chunked && h->in_trailer) { + /* If "chunked" then there is always a trailer, + * consisting of zero or more non-empty lines + * ending with CR LF and then an empty line w/ CR LF. + * We do NOT support trailers except to skip them -- + * this is supported (generally) by the http spec. + */ + bbp = h->body + h->body_nbytes; + for (;;) { + int c; + if (nread <= 0) + break; + c = *rbp++; + nread--; + switch (h->trailer_state) { + case trailer_line_begin: + if (c == '\r') + h->trailer_state = trailer_empty_cr; + else + h->trailer_state = trailer_nonempty; + break; + case trailer_empty_cr: + /* end empty line */ + if (c == '\n') { + h->trailer_state = trailer_line_begin; + h->in_trailer = 0; + if (httpread_debug >= 10) + wpa_printf( + MSG_DEBUG, + "httpread got content(%p)", h); + h->got_body = 1; + goto got_file; + } + h->trailer_state = trailer_nonempty; + break; + case trailer_nonempty: + if (c == '\r') + h->trailer_state = trailer_nonempty_cr; + break; + case trailer_nonempty_cr: + if (c == '\n') + h->trailer_state = trailer_line_begin; + else + h->trailer_state = trailer_nonempty; + break; + } + } + } + goto get_more; + +bad: + /* Error */ + wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); + return; + +get_more: + return; + +got_file: + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + /* Null terminate for convenience of some applications */ + if (h->body) + h->body[h->body_nbytes] = 0; /* null terminate */ + h->got_file = 1; + /* Assume that we do NOT support keeping connection alive, + * and just in case somehow we don't get destroyed right away, + * unregister now. + */ + if (h->sd_registered) + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + h->sd_registered = 0; + /* The application can destroy us whenever they feel like... + * cancel timeout. + */ + if (h->to_registered) + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + h->to_registered = 0; + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); +} + + +/* httpread_create -- start a new reading session making use of eloop. + * The new instance will use the socket descriptor for reading (until + * it gets a file and not after) but will not close the socket, even + * when the instance is destroyed (the application must do that). + * Return NULL on error. + * + * Provided that httpread_create successfully returns a handle, + * the callback fnc is called to handle httpread_event events. + * The caller should do destroy on any errors or unknown events. + * + * Pass max_bytes == 0 to not read body at all (required for e.g. + * reply to HEAD request). + */ +struct httpread * httpread_create( + int sd, /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e), /* call on event */ + void *cookie, /* pass to callback */ + int max_bytes, /* maximum body size else abort it */ + int timeout_seconds /* 0; or total duration timeout period */ + ) +{ + struct httpread *h = NULL; + + h = os_zalloc(sizeof(*h)); + if (h == NULL) + goto fail; + h->sd = sd; + h->cb = cb; + h->cookie = cookie; + h->max_bytes = max_bytes; + h->timeout_seconds = timeout_seconds; + + if (timeout_seconds > 0) { + if (eloop_register_timeout(timeout_seconds, 0, + httpread_timeout_handler, + NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + h->to_registered = 1; + } + if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, + NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + h->sd_registered = 1; + return h; + +fail: + + /* Error */ + httpread_destroy(h); + return NULL; +} + + +/* httpread_hdr_type_get -- When file is ready, returns header type. */ +enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) +{ + return h->hdr_type; +} + + +/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI + * or possibly NULL (which would be an error). + */ +char * httpread_uri_get(struct httpread *h) +{ + return h->uri; +} + + +/* httpread_reply_code_get -- When reply is ready, returns reply code */ +int httpread_reply_code_get(struct httpread *h) +{ + return h->reply_code; +} + + +/* httpread_length_get -- When file is ready, returns file length. */ +int httpread_length_get(struct httpread *h) +{ + return h->body_nbytes; +} + + +/* httpread_data_get -- When file is ready, returns file content + * with null byte appened. + * Might return NULL in some error condition. + */ +void * httpread_data_get(struct httpread *h) +{ + return h->body ? h->body : ""; +} + + +/* httpread_hdr_get -- When file is ready, returns header content + * with null byte appended. + * Might return NULL in some error condition. + */ +char * httpread_hdr_get(struct httpread *h) +{ + return h->hdr; +} + + +/* httpread_hdr_line_get -- When file is ready, returns pointer + * to line within header content matching the given tag + * (after the tag itself and any spaces/tabs). + * + * The tag should end with a colon for reliable matching. + * + * If not found, returns NULL; + */ +char * httpread_hdr_line_get(struct httpread *h, const char *tag) +{ + int tag_len = os_strlen(tag); + char *hdr = h->hdr; + hdr = os_strchr(hdr, '\n'); + if (hdr == NULL) + return NULL; + hdr++; + for (;;) { + if (!os_strncasecmp(hdr, tag, tag_len)) { + hdr += tag_len; + while (*hdr == ' ' || *hdr == '\t') + hdr++; + return hdr; + } + hdr = os_strchr(hdr, '\n'); + if (hdr == NULL) + return NULL; + hdr++; + } +} diff --git a/src/wps/httpread.h b/src/wps/httpread.h new file mode 100644 index 0000000000000..fb1ecb7de36cf --- /dev/null +++ b/src/wps/httpread.h @@ -0,0 +1,123 @@ +/** + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HTTPREAD_H +#define HTTPREAD_H + +/* event types (passed to callback) */ +enum httpread_event { + HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */ + HTTPREAD_EVENT_TIMEOUT = 2, + HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */ +}; + + +/* header type detected + * available to callback via call to httpread_reply_code_get() + */ +enum httpread_hdr_type { + HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */ + HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */ + HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET<sp> */ + HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD<sp> */ + HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST<sp> */ + HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */ + + HTTPREAD_N_HDR_TYPES /* keep last */ +}; + + +/* control instance -- opaque struct declaration + */ +struct httpread; + + +/* httpread_destroy -- if h is non-NULL, clean up + * This must eventually be called by the application following + * call of the application's callback and may be called + * earlier if desired. + */ +void httpread_destroy(struct httpread *h); + +/* httpread_create -- start a new reading session making use of eloop. + * The new instance will use the socket descriptor for reading (until + * it gets a file and not after) but will not close the socket, even + * when the instance is destroyed (the application must do that). + * Return NULL on error. + * + * Provided that httpread_create successfully returns a handle, + * the callback fnc is called to handle httpread_event events. + * The caller should do destroy on any errors or unknown events. + * + * Pass max_bytes == 0 to not read body at all (required for e.g. + * reply to HEAD request). + */ +struct httpread * httpread_create( + int sd, /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e), /* call on event */ + void *cookie, /* pass to callback */ + int max_bytes, /* maximum file size else abort it */ + int timeout_seconds /* 0; or total duration timeout period */ + ); + +/* httpread_hdr_type_get -- When file is ready, returns header type. + */ +enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h); + + +/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI + * or possibly NULL (which would be an error). + */ +char *httpread_uri_get(struct httpread *h); + +/* httpread_reply_code_get -- When reply is ready, returns reply code */ +int httpread_reply_code_get(struct httpread *h); + + +/* httpread_length_get -- When file is ready, returns file length. */ +int httpread_length_get(struct httpread *h); + +/* httpread_data_get -- When file is ready, returns file content + * with null byte appened. + * Might return NULL in some error condition. + */ +void * httpread_data_get(struct httpread *h); + +/* httpread_hdr_get -- When file is ready, returns header content + * with null byte appended. + * Might return NULL in some error condition. + */ +char * httpread_hdr_get(struct httpread *h); + +/* httpread_hdr_line_get -- When file is ready, returns pointer + * to line within header content matching the given tag + * (after the tag itself and any spaces/tabs). + * + * The tag should end with a colon for reliable matching. + * + * If not found, returns NULL; + */ +char * httpread_hdr_line_get(struct httpread *h, const char *tag); + +#endif /* HTTPREAD_H */ diff --git a/src/wps/wps.c b/src/wps/wps.c new file mode 100644 index 0000000000000..395eba6af29fe --- /dev/null +++ b/src/wps/wps.c @@ -0,0 +1,335 @@ +/* + * Wi-Fi Protected Setup + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_i.h" +#include "wps_dev_attr.h" +#include "ieee802_11_defs.h" + + +/** + * wps_init - Initialize WPS Registration protocol data + * @cfg: WPS configuration + * Returns: Pointer to allocated data or %NULL on failure + * + * This function is used to initialize WPS data for a registration protocol + * instance (i.e., each run of registration protocol as a Registrar of + * Enrollee. The caller is responsible for freeing this data after the + * registration run has been completed by calling wps_deinit(). + */ +struct wps_data * wps_init(const struct wps_config *cfg) +{ + struct wps_data *data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->wps = cfg->wps; + data->registrar = cfg->registrar; + if (cfg->registrar) { + os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN); + } else { + os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN); + os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); + } + if (cfg->pin) { + data->dev_pw_id = DEV_PW_DEFAULT; + data->dev_password = os_malloc(cfg->pin_len); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->dev_password, cfg->pin, cfg->pin_len); + data->dev_password_len = cfg->pin_len; + } + + data->pbc = cfg->pbc; + if (cfg->pbc) { + /* Use special PIN '00000000' for PBC */ + data->dev_pw_id = DEV_PW_PUSHBUTTON; + os_free(data->dev_password); + data->dev_password = os_malloc(8); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memset(data->dev_password, '0', 8); + data->dev_password_len = 8; + } + + data->state = data->registrar ? RECV_M1 : SEND_M1; + + if (cfg->assoc_wps_ie) { + struct wps_parse_attr attr; + wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq", + cfg->assoc_wps_ie); + if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE " + "from (Re)AssocReq"); + } else if (attr.request_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute " + "in (Re)AssocReq WPS IE"); + } else { + wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE " + "in (Re)AssocReq WPS IE): %d", + *attr.request_type); + data->request_type = *attr.request_type; + } + } + + return data; +} + + +/** + * wps_deinit - Deinitialize WPS Registration protocol data + * @data: WPS Registration protocol data from wps_init() + */ +void wps_deinit(struct wps_data *data) +{ + if (data->wps_pin_revealed) { + wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and " + "negotiation failed"); + if (data->registrar) + wps_registrar_invalidate_pin(data->wps->registrar, + data->uuid_e); + } else if (data->registrar) + wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e); + + wpabuf_free(data->dh_privkey); + wpabuf_free(data->dh_pubkey_e); + wpabuf_free(data->dh_pubkey_r); + wpabuf_free(data->last_msg); + os_free(data->dev_password); + os_free(data->new_psk); + wps_device_data_free(&data->peer_dev); + os_free(data); +} + + +/** + * wps_process_msg - Process a WPS message + * @wps: WPS Registration protocol data from wps_init() + * @op_code: Message OP Code + * @msg: Message data + * Returns: Processing result + * + * This function is used to process WPS messages with OP Codes WSC_ACK, + * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is + * responsible for reassembling the messages before calling this function. + * Response to this message is built by calling wps_get_msg(). + */ +enum wps_process_res wps_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg) +{ + if (wps->registrar) + return wps_registrar_process_msg(wps, op_code, msg); + else + return wps_enrollee_process_msg(wps, op_code, msg); +} + + +/** + * wps_get_msg - Build a WPS message + * @wps: WPS Registration protocol data from wps_init() + * @op_code: Buffer for returning message OP Code + * Returns: The generated WPS message or %NULL on failure + * + * This function is used to build a response to a message processed by calling + * wps_process_msg(). The caller is responsible for freeing the buffer. + */ +struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) +{ + if (wps->registrar) + return wps_registrar_get_msg(wps, op_code); + else + return wps_enrollee_get_msg(wps, op_code); +} + + +/** + * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC + * @msg: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if PBC Registrar is active, 0 if not + */ +int wps_is_selected_pbc_registrar(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + /* + * In theory, this could also verify that attr.sel_reg_config_methods + * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations + * do not set Selected Registrar Config Methods attribute properly, so + * it is safer to just use Device Password ID here. + */ + + if (wps_parse_msg(msg, &attr) < 0 || + !attr.selected_registrar || *attr.selected_registrar == 0 || + !attr.dev_password_id || + WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) + return 0; + + return 1; +} + + +/** + * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN + * @msg: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if PIN Registrar is active, 0 if not + */ +int wps_is_selected_pin_registrar(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + /* + * In theory, this could also verify that attr.sel_reg_config_methods + * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, + * but some deployed AP implementations do not set Selected Registrar + * Config Methods attribute properly, so it is safer to just use + * Device Password ID here. + */ + + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + if (!attr.selected_registrar || *attr.selected_registrar == 0) + return 0; + + if (attr.dev_password_id != NULL && + WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON) + return 0; + + return 1; +} + + +/** + * wps_get_uuid_e - Get UUID-E from WPS IE + * @msg: WPS IE contents from Beacon or Probe Response frame + * Returns: Pointer to UUID-E or %NULL if not included + * + * The returned pointer is to the msg contents and it remains valid only as + * long as the msg buffer is valid. + */ +const u8 * wps_get_uuid_e(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(msg, &attr) < 0) + return NULL; + return attr.uuid_e; +} + + +/** + * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request + * @req_type: Value for Request Type attribute + * Returns: WPS IE or %NULL on failure + * + * The caller is responsible for freeing the buffer. + */ +struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) +{ + struct wpabuf *ie; + u8 *len; + + wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association " + "Request"); + ie = wpabuf_alloc(100); + if (ie == NULL) + return NULL; + + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(ie, 1); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + + if (wps_build_version(ie) || + wps_build_req_type(ie, req_type)) { + wpabuf_free(ie); + return NULL; + } + + *len = wpabuf_len(ie) - 2; + + return ie; +} + + +/** + * wps_build_probe_req_ie - Build WPS IE for Probe Request + * @pbc: Whether searching for PBC mode APs + * @dev: Device attributes + * @uuid: Own UUID + * @req_type: Value for Request Type attribute + * Returns: WPS IE or %NULL on failure + * + * The caller is responsible for freeing the buffer. + */ +struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, + const u8 *uuid, + enum wps_request_type req_type) +{ + struct wpabuf *ie; + u8 *len; + u16 methods; + + wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request"); + + ie = wpabuf_alloc(200); + if (ie == NULL) + return NULL; + + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(ie, 1); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + + if (pbc) + methods = WPS_CONFIG_PUSHBUTTON; + else + methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | + WPS_CONFIG_KEYPAD; + + if (wps_build_version(ie) || + wps_build_req_type(ie, req_type) || + wps_build_config_methods(ie, methods) || + wps_build_uuid_e(ie, uuid) || + wps_build_primary_dev_type(dev, ie) || + wps_build_rf_bands(dev, ie) || + wps_build_assoc_state(NULL, ie) || + wps_build_config_error(ie, WPS_CFG_NO_ERROR) || + wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON : + DEV_PW_DEFAULT)) { + wpabuf_free(ie); + return NULL; + } + + *len = wpabuf_len(ie) - 2; + + return ie; +} + + +void wps_free_pending_msgs(struct upnp_pending_message *msgs) +{ + struct upnp_pending_message *p, *prev; + p = msgs; + while (p) { + prev = p; + p = p->next; + wpabuf_free(prev->msg); + os_free(prev); + } +} diff --git a/src/wps/wps.h b/src/wps/wps.h new file mode 100644 index 0000000000000..e0f2b2d6e06ea --- /dev/null +++ b/src/wps/wps.h @@ -0,0 +1,518 @@ +/* + * Wi-Fi Protected Setup + * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_H +#define WPS_H + +#include "wps_defs.h" + +/** + * enum wsc_op_code - EAP-WSC OP-Code values + */ +enum wsc_op_code { + WSC_UPnP = 0 /* No OP Code in UPnP transport */, + WSC_Start = 0x01, + WSC_ACK = 0x02, + WSC_NACK = 0x03, + WSC_MSG = 0x04, + WSC_Done = 0x05, + WSC_FRAG_ACK = 0x06 +}; + +struct wps_registrar; +struct upnp_wps_device_sm; + +/** + * struct wps_credential - WPS Credential + * @ssid: SSID + * @ssid_len: Length of SSID + * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags) + * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags) + * @key_idx: Key index + * @key: Key + * @key_len: Key length in octets + * @mac_addr: MAC address of the peer + * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); + * this may be %NULL, if not used + * @cred_attr_len: Length of cred_attr in octets + */ +struct wps_credential { + u8 ssid[32]; + size_t ssid_len; + u16 auth_type; + u16 encr_type; + u8 key_idx; + u8 key[64]; + size_t key_len; + u8 mac_addr[ETH_ALEN]; + const u8 *cred_attr; + size_t cred_attr_len; +}; + +/** + * struct wps_device_data - WPS Device Data + * @mac_addr: Device MAC address + * @device_name: Device Name (0..32 octets encoded in UTF-8) + * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8) + * @model_name: Model Name (0..32 octets encoded in UTF-8) + * @model_number: Model Number (0..32 octets encoded in UTF-8) + * @serial_number: Serial Number (0..32 octets encoded in UTF-8) + * @categ: Primary Device Category + * @oui: Primary Device OUI + * @sub_categ: Primary Device Sub-Category + * @os_version: OS Version + * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + */ +struct wps_device_data { + u8 mac_addr[ETH_ALEN]; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + u16 categ; + u32 oui; + u16 sub_categ; + u32 os_version; + u8 rf_bands; +}; + +/** + * struct wps_config - WPS configuration for a single registration protocol run + */ +struct wps_config { + /** + * wps - Pointer to long term WPS context + */ + struct wps_context *wps; + + /** + * registrar - Whether this end is a Registrar + */ + int registrar; + + /** + * pin - Enrollee Device Password (%NULL for Registrar or PBC) + */ + const u8 *pin; + + /** + * pin_len - Length on pin in octets + */ + size_t pin_len; + + /** + * pbc - Whether this is protocol run uses PBC + */ + int pbc; + + /** + * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP) + */ + const struct wpabuf *assoc_wps_ie; +}; + +struct wps_data * wps_init(const struct wps_config *cfg); + +void wps_deinit(struct wps_data *data); + +/** + * enum wps_process_res - WPS message processing result + */ +enum wps_process_res { + /** + * WPS_DONE - Processing done + */ + WPS_DONE, + + /** + * WPS_CONTINUE - Processing continues + */ + WPS_CONTINUE, + + /** + * WPS_FAILURE - Processing failed + */ + WPS_FAILURE, + + /** + * WPS_PENDING - Processing continues, but waiting for an external + * event (e.g., UPnP message from an external Registrar) + */ + WPS_PENDING +}; +enum wps_process_res wps_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg); + +struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code); + +int wps_is_selected_pbc_registrar(const struct wpabuf *msg); +int wps_is_selected_pin_registrar(const struct wpabuf *msg); +const u8 * wps_get_uuid_e(const struct wpabuf *msg); + +struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); +struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, + const u8 *uuid, + enum wps_request_type req_type); + + +/** + * struct wps_registrar_config - WPS Registrar configuration + */ +struct wps_registrar_config { + /** + * new_psk_cb - Callback for new PSK + * @ctx: Higher layer context data (cb_ctx) + * @mac_addr: MAC address of the Enrollee + * @psk: The new PSK + * @psk_len: The length of psk in octets + * Returns: 0 on success, -1 on failure + * + * This callback is called when a new per-device PSK is provisioned. + */ + int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len); + + /** + * set_ie_cb - Callback for WPS IE changes + * @ctx: Higher layer context data (cb_ctx) + * @beacon_ie: WPS IE for Beacon + * @beacon_ie_len: WPS IE length for Beacon + * @probe_resp_ie: WPS IE for Probe Response + * @probe_resp_ie_len: WPS IE length for Probe Response + * Returns: 0 on success, -1 on failure + * + * This callback is called whenever the WPS IE in Beacon or Probe + * Response frames needs to be changed (AP only). + */ + int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len, + const u8 *probe_resp_ie, size_t probe_resp_ie_len); + + /** + * pin_needed_cb - Callback for requesting a PIN + * @ctx: Higher layer context data (cb_ctx) + * @uuid_e: UUID-E of the unknown Enrollee + * @dev: Device Data from the unknown Enrollee + * + * This callback is called whenever an unknown Enrollee requests to use + * PIN method and a matching PIN (Device Password) is not found in + * Registrar data. + */ + void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev); + + /** + * reg_success_cb - Callback for reporting successful registration + * @ctx: Higher layer context data (cb_ctx) + * @mac_addr: MAC address of the Enrollee + * @uuid_e: UUID-E of the Enrollee + * + * This callback is called whenever an Enrollee completes registration + * successfully. + */ + void (*reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e); + + /** + * cb_ctx: Higher layer context data for Registrar callbacks + */ + void *cb_ctx; + + /** + * skip_cred_build: Do not build credential + * + * This option can be used to disable internal code that builds + * Credential attribute into M8 based on the current network + * configuration and Enrollee capabilities. The extra_cred data will + * then be used as the Credential(s). + */ + int skip_cred_build; + + /** + * extra_cred: Additional Credential attribute(s) + * + * This optional data (set to %NULL to disable) can be used to add + * Credential attribute(s) for other networks into M8. If + * skip_cred_build is set, this will also override the automatically + * generated Credential attribute. + */ + const u8 *extra_cred; + + /** + * extra_cred_len: Length of extra_cred in octets + */ + size_t extra_cred_len; + + /** + * disable_auto_conf - Disable auto-configuration on first registration + * + * By default, the AP that is started in not configured state will + * generate a random PSK and move to configured state when the first + * registration protocol run is completed successfully. This option can + * be used to disable this functionality and leave it up to an external + * program to take care of configuration. This requires the extra_cred + * to be set with a suitable Credential and skip_cred_build being used. + */ + int disable_auto_conf; +}; + + +/** + * enum wps_event - WPS event types + */ +enum wps_event { + /** + * WPS_EV_M2D - M2D received (Registrar did not know us) + */ + WPS_EV_M2D, + + /** + * WPS_EV_FAIL - Registration failed + */ + WPS_EV_FAIL, + + /** + * WPS_EV_SUCCESS - Registration succeeded + */ + WPS_EV_SUCCESS, + + /** + * WPS_EV_PWD_AUTH_FAIL - Password authentication failed + */ + WPS_EV_PWD_AUTH_FAIL +}; + +/** + * union wps_event_data - WPS event data + */ +union wps_event_data { + /** + * struct wps_event_m2d - M2D event data + */ + struct wps_event_m2d { + u16 config_methods; + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + const u8 *dev_name; + size_t dev_name_len; + const u8 *primary_dev_type; /* 8 octets */ + u16 config_error; + u16 dev_password_id; + } m2d; + + /** + * struct wps_event_fail - Registration failure information + * @msg: enum wps_msg_type + */ + struct wps_event_fail { + int msg; + } fail; + + struct wps_event_pwd_auth_fail { + int enrollee; + int part; + } pwd_auth_fail; +}; + +/** + * struct upnp_pending_message - Pending PutWLANResponse messages + * @next: Pointer to next pending message or %NULL + * @addr: NewWLANEventMAC + * @msg: NewMessage + * @type: Message Type + */ +struct upnp_pending_message { + struct upnp_pending_message *next; + u8 addr[ETH_ALEN]; + struct wpabuf *msg; + enum wps_msg_type type; +}; + +/** + * struct wps_context - Long term WPS context data + * + * This data is stored at the higher layer Authenticator or Supplicant data + * structures and it is maintained over multiple registration protocol runs. + */ +struct wps_context { + /** + * ap - Whether the local end is an access point + */ + int ap; + + /** + * registrar - Pointer to WPS registrar data from wps_registrar_init() + */ + struct wps_registrar *registrar; + + /** + * wps_state - Current WPS state + */ + enum wps_state wps_state; + + /** + * ap_setup_locked - Whether AP setup is locked (only used at AP) + */ + int ap_setup_locked; + + /** + * uuid - Own UUID + */ + u8 uuid[16]; + + /** + * ssid - SSID + * + * This SSID is used by the Registrar to fill in information for + * Credentials. In addition, AP uses it when acting as an Enrollee to + * notify Registrar of the current configuration. + */ + u8 ssid[32]; + + /** + * ssid_len - Length of ssid in octets + */ + size_t ssid_len; + + /** + * dev - Own WPS device data + */ + struct wps_device_data dev; + + /** + * config_methods - Enabled configuration methods + * + * Bit field of WPS_CONFIG_* + */ + u16 config_methods; + + /** + * encr_types - Enabled encryption types (bit field of WPS_ENCR_*) + */ + u16 encr_types; + + /** + * auth_types - Authentication types (bit field of WPS_AUTH_*) + */ + u16 auth_types; + + /** + * network_key - The current Network Key (PSK) or %NULL to generate new + * + * If %NULL, Registrar will generate per-device PSK. In addition, AP + * uses this when acting as an Enrollee to notify Registrar of the + * current configuration. + */ + u8 *network_key; + + /** + * network_key_len - Length of network_key in octets + */ + size_t network_key_len; + + /** + * ap_settings - AP Settings override for M7 (only used at AP) + * + * If %NULL, AP Settings attributes will be generated based on the + * current network configuration. + */ + u8 *ap_settings; + + /** + * ap_settings_len - Length of ap_settings in octets + */ + size_t ap_settings_len; + + /** + * friendly_name - Friendly Name (required for UPnP) + */ + char *friendly_name; + + /** + * manufacturer_url - Manufacturer URL (optional for UPnP) + */ + char *manufacturer_url; + + /** + * model_description - Model Description (recommended for UPnP) + */ + char *model_description; + + /** + * model_url - Model URL (optional for UPnP) + */ + char *model_url; + + /** + * upc - Universal Product Code (optional for UPnP) + */ + char *upc; + + /** + * cred_cb - Callback to notify that new Credentials were received + * @ctx: Higher layer context data (cb_ctx) + * @cred: The received Credential + * Return: 0 on success, -1 on failure + */ + int (*cred_cb)(void *ctx, const struct wps_credential *cred); + + /** + * event_cb - Event callback (state information about progress) + * @ctx: Higher layer context data (cb_ctx) + * @event: Event type + * @data: Event data + */ + void (*event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); + + /** + * cb_ctx: Higher layer context data for callbacks + */ + void *cb_ctx; + + struct upnp_wps_device_sm *wps_upnp; + + /* Pending messages from UPnP PutWLANResponse */ + struct upnp_pending_message *upnp_msgs; +}; + + +struct wps_registrar * +wps_registrar_init(struct wps_context *wps, + const struct wps_registrar_config *cfg); +void wps_registrar_deinit(struct wps_registrar *reg); +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, + const u8 *pin, size_t pin_len); +int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); +int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); +int wps_registrar_button_pushed(struct wps_registrar *reg); +void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, + const struct wpabuf *wps_data); +int wps_registrar_update_ie(struct wps_registrar *reg); +int wps_registrar_set_selected_registrar(struct wps_registrar *reg, + const struct wpabuf *msg); + +unsigned int wps_pin_checksum(unsigned int pin); +unsigned int wps_pin_valid(unsigned int pin); +unsigned int wps_generate_pin(void); +void wps_free_pending_msgs(struct upnp_pending_message *msgs); + +#endif /* WPS_H */ diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c new file mode 100644 index 0000000000000..edeff5c58df45 --- /dev/null +++ b/src/wps/wps_attr_build.c @@ -0,0 +1,254 @@ +/* + * Wi-Fi Protected Setup - attribute building + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "dh_groups.h" +#include "sha256.h" +#include "aes_wrap.h" +#include "wps_i.h" + + +int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) +{ + struct wpabuf *pubkey; + + wpa_printf(MSG_DEBUG, "WPS: * Public Key"); + pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey); + pubkey = wpabuf_zeropad(pubkey, 192); + if (pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to initialize " + "Diffie-Hellman handshake"); + return -1; + } + + wpabuf_put_be16(msg, ATTR_PUBLIC_KEY); + wpabuf_put_be16(msg, wpabuf_len(pubkey)); + wpabuf_put_buf(msg, pubkey); + + if (wps->registrar) { + wpabuf_free(wps->dh_pubkey_r); + wps->dh_pubkey_r = pubkey; + } else { + wpabuf_free(wps->dh_pubkey_e); + wps->dh_pubkey_e = pubkey; + } + + return 0; +} + + +int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type) +{ + wpa_printf(MSG_DEBUG, "WPS: * Request Type"); + wpabuf_put_be16(msg, ATTR_REQUEST_TYPE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, type); + return 0; +} + + +int wps_build_config_methods(struct wpabuf *msg, u16 methods) +{ + wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); + wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, methods); + return 0; +} + + +int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid) +{ + wpa_printf(MSG_DEBUG, "WPS: * UUID-E"); + wpabuf_put_be16(msg, ATTR_UUID_E); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, uuid, WPS_UUID_LEN); + return 0; +} + + +int wps_build_dev_password_id(struct wpabuf *msg, u16 id) +{ + wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id); + wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, id); + return 0; +} + + +int wps_build_config_error(struct wpabuf *msg, u16 err) +{ + wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err); + wpabuf_put_be16(msg, ATTR_CONFIG_ERROR); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, err); + return 0; +} + + +int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + if (wps->last_msg == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Last message not available for " + "building authenticator"); + return -1; + } + + /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*) + * (M_curr* is M_curr without the Authenticator attribute) + */ + addr[0] = wpabuf_head(wps->last_msg); + len[0] = wpabuf_len(wps->last_msg); + addr[1] = wpabuf_head(msg); + len[1] = wpabuf_len(msg); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash); + + wpa_printf(MSG_DEBUG, "WPS: * Authenticator"); + wpabuf_put_be16(msg, ATTR_AUTHENTICATOR); + wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN); + wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN); + + return 0; +} + + +int wps_build_version(struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Version"); + wpabuf_put_be16(msg, ATTR_VERSION); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, WPS_VERSION); + return 0; +} + + +int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type) +{ + wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type); + wpabuf_put_be16(msg, ATTR_MSG_TYPE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, msg_type); + return 0; +} + + +int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce"); + wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN); + return 0; +} + + +int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce"); + wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN); + return 0; +} + + +int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); + wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, WPS_AUTH_TYPES); + return 0; +} + + +int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); + wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, WPS_ENCR_TYPES); + return 0; +} + + +int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags"); + wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, WPS_CONN_ESS); + return 0; +} + + +int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Association State"); + wpabuf_put_be16(msg, ATTR_ASSOC_STATE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC); + return 0; +} + + +int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg) +{ + u8 hash[SHA256_MAC_LEN]; + + wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator"); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg), + wpabuf_len(msg), hash); + + wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH); + wpabuf_put_be16(msg, WPS_KWA_LEN); + wpabuf_put_data(msg, hash, WPS_KWA_LEN); + return 0; +} + + +int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, + struct wpabuf *plain) +{ + size_t pad_len; + const size_t block_size = 16; + u8 *iv, *data; + + wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings"); + + /* PKCS#5 v2.0 pad */ + pad_len = block_size - wpabuf_len(plain) % block_size; + os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len); + + wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS); + wpabuf_put_be16(msg, block_size + wpabuf_len(plain)); + + iv = wpabuf_put(msg, block_size); + if (os_get_random(iv, block_size) < 0) + return -1; + + data = wpabuf_put(msg, 0); + wpabuf_put_buf(msg, plain); + if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain))) + return -1; + + return 0; +} diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c new file mode 100644 index 0000000000000..25ff25189076c --- /dev/null +++ b/src/wps/wps_attr_parse.c @@ -0,0 +1,429 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_i.h" + + +static int wps_set_attr(struct wps_parse_attr *attr, u16 type, + const u8 *pos, u16 len) +{ + switch (type) { + case ATTR_VERSION: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u", + len); + return -1; + } + attr->version = pos; + break; + case ATTR_MSG_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type " + "length %u", len); + return -1; + } + attr->msg_type = pos; + break; + case ATTR_ENROLLEE_NONCE: + if (len != WPS_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce " + "length %u", len); + return -1; + } + attr->enrollee_nonce = pos; + break; + case ATTR_REGISTRAR_NONCE: + if (len != WPS_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce " + "length %u", len); + return -1; + } + attr->registrar_nonce = pos; + break; + case ATTR_UUID_E: + if (len != WPS_UUID_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u", + len); + return -1; + } + attr->uuid_e = pos; + break; + case ATTR_UUID_R: + if (len != WPS_UUID_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u", + len); + return -1; + } + attr->uuid_r = pos; + break; + case ATTR_AUTH_TYPE_FLAGS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " + "Type Flags length %u", len); + return -1; + } + attr->auth_type_flags = pos; + break; + case ATTR_ENCR_TYPE_FLAGS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type " + "Flags length %u", len); + return -1; + } + attr->encr_type_flags = pos; + break; + case ATTR_CONN_TYPE_FLAGS: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type " + "Flags length %u", len); + return -1; + } + attr->conn_type_flags = pos; + break; + case ATTR_CONFIG_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods " + "length %u", len); + return -1; + } + attr->config_methods = pos; + break; + case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Selected " + "Registrar Config Methods length %u", len); + return -1; + } + attr->sel_reg_config_methods = pos; + break; + case ATTR_PRIMARY_DEV_TYPE: + if (len != sizeof(struct wps_dev_type)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device " + "Type length %u", len); + return -1; + } + attr->primary_dev_type = pos; + break; + case ATTR_RF_BANDS: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length " + "%u", len); + return -1; + } + attr->rf_bands = pos; + break; + case ATTR_ASSOC_STATE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Association State " + "length %u", len); + return -1; + } + attr->assoc_state = pos; + break; + case ATTR_CONFIG_ERROR: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration " + "Error length %u", len); + return -1; + } + attr->config_error = pos; + break; + case ATTR_DEV_PASSWORD_ID: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password " + "ID length %u", len); + return -1; + } + attr->dev_password_id = pos; + break; + case ATTR_OS_VERSION: + if (len != 4) { + wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " + "%u", len); + return -1; + } + attr->os_version = pos; + break; + case ATTR_WPS_STATE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected " + "Setup State length %u", len); + return -1; + } + attr->wps_state = pos; + break; + case ATTR_AUTHENTICATOR: + if (len != WPS_AUTHENTICATOR_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator " + "length %u", len); + return -1; + } + attr->authenticator = pos; + break; + case ATTR_R_HASH1: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u", + len); + return -1; + } + attr->r_hash1 = pos; + break; + case ATTR_R_HASH2: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u", + len); + return -1; + } + attr->r_hash2 = pos; + break; + case ATTR_E_HASH1: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u", + len); + return -1; + } + attr->e_hash1 = pos; + break; + case ATTR_E_HASH2: + if (len != WPS_HASH_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u", + len); + return -1; + } + attr->e_hash2 = pos; + break; + case ATTR_R_SNONCE1: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length " + "%u", len); + return -1; + } + attr->r_snonce1 = pos; + break; + case ATTR_R_SNONCE2: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length " + "%u", len); + return -1; + } + attr->r_snonce2 = pos; + break; + case ATTR_E_SNONCE1: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length " + "%u", len); + return -1; + } + attr->e_snonce1 = pos; + break; + case ATTR_E_SNONCE2: + if (len != WPS_SECRET_NONCE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length " + "%u", len); + return -1; + } + attr->e_snonce2 = pos; + break; + case ATTR_KEY_WRAP_AUTH: + if (len != WPS_KWA_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap " + "Authenticator length %u", len); + return -1; + } + attr->key_wrap_auth = pos; + break; + case ATTR_AUTH_TYPE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication " + "Type length %u", len); + return -1; + } + attr->auth_type = pos; + break; + case ATTR_ENCR_TYPE: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption " + "Type length %u", len); + return -1; + } + attr->encr_type = pos; + break; + case ATTR_NETWORK_INDEX: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index " + "length %u", len); + return -1; + } + attr->network_idx = pos; + break; + case ATTR_NETWORK_KEY_INDEX: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index " + "length %u", len); + return -1; + } + attr->network_key_idx = pos; + break; + case ATTR_MAC_ADDR: + if (len != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address " + "length %u", len); + return -1; + } + attr->mac_addr = pos; + break; + case ATTR_KEY_PROVIDED_AUTO: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided " + "Automatically length %u", len); + return -1; + } + attr->key_prov_auto = pos; + break; + case ATTR_802_1X_ENABLED: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled " + "length %u", len); + return -1; + } + attr->dot1x_enabled = pos; + break; + case ATTR_SELECTED_REGISTRAR: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar" + " length %u", len); + return -1; + } + attr->selected_registrar = pos; + break; + case ATTR_REQUEST_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type " + "length %u", len); + return -1; + } + attr->request_type = pos; + break; + case ATTR_RESPONSE_TYPE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type " + "length %u", len); + return -1; + } + attr->request_type = pos; + break; + case ATTR_MANUFACTURER: + attr->manufacturer = pos; + attr->manufacturer_len = len; + break; + case ATTR_MODEL_NAME: + attr->model_name = pos; + attr->model_name_len = len; + break; + case ATTR_MODEL_NUMBER: + attr->model_number = pos; + attr->model_number_len = len; + break; + case ATTR_SERIAL_NUMBER: + attr->serial_number = pos; + attr->serial_number_len = len; + break; + case ATTR_DEV_NAME: + attr->dev_name = pos; + attr->dev_name_len = len; + break; + case ATTR_PUBLIC_KEY: + attr->public_key = pos; + attr->public_key_len = len; + break; + case ATTR_ENCR_SETTINGS: + attr->encr_settings = pos; + attr->encr_settings_len = len; + break; + case ATTR_CRED: + if (attr->num_cred >= MAX_CRED_COUNT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Credential " + "attribute (max %d credentials)", + MAX_CRED_COUNT); + break; + } + attr->cred[attr->num_cred] = pos; + attr->cred_len[attr->num_cred] = len; + attr->num_cred++; + break; + case ATTR_SSID: + attr->ssid = pos; + attr->ssid_len = len; + break; + case ATTR_NETWORK_KEY: + attr->network_key = pos; + attr->network_key_len = len; + break; + case ATTR_EAP_TYPE: + attr->eap_type = pos; + attr->eap_type_len = len; + break; + case ATTR_EAP_IDENTITY: + attr->eap_identity = pos; + attr->eap_identity_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " + "len=%u", type, len); + break; + } + + return 0; +} + + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) +{ + const u8 *pos, *end; + u16 type, len; + + os_memset(attr, 0, sizeof(*attr)); + pos = wpabuf_head(msg); + end = pos + wpabuf_len(msg); + + while (pos < end) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "WPS: Invalid message - " + "%lu bytes remaining", + (unsigned long) (end - pos)); + return -1; + } + + type = WPA_GET_BE16(pos); + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u", + type, len); + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "WPS: Attribute overflow"); + return -1; + } + + if (wps_set_attr(attr, type, pos, len) < 0) + return -1; + + pos += len; + } + + return 0; +} diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c new file mode 100644 index 0000000000000..ae6e906c265a2 --- /dev/null +++ b/src/wps/wps_attr_process.c @@ -0,0 +1,323 @@ +/* + * Wi-Fi Protected Setup - attribute processing + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "wps_i.h" + + +int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, + const struct wpabuf *msg) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + if (authenticator == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute " + "included"); + return -1; + } + + if (wps->last_msg == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Last message not available for " + "validating authenticator"); + return -1; + } + + /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*) + * (M_curr* is M_curr without the Authenticator attribute) + */ + addr[0] = wpabuf_head(wps->last_msg); + len[0] = wpabuf_len(wps->last_msg); + addr[1] = wpabuf_head(msg); + len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN; + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash); + + if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator"); + return -1; + } + + return 0; +} + + +int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg, + const u8 *key_wrap_auth) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *head; + size_t len; + + if (key_wrap_auth == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute"); + return -1; + } + + head = wpabuf_head(msg); + len = wpabuf_len(msg) - 4 - WPS_KWA_LEN; + if (head + len != key_wrap_auth - 4) { + wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the " + "decrypted attribute"); + return -1; + } + + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash); + if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid KWA"); + return -1; + } + + return 0; +} + + +static int wps_process_cred_network_idx(struct wps_credential *cred, + const u8 *idx) +{ + if (idx == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include " + "Network Index"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx); + + return 0; +} + + +static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid, + size_t ssid_len) +{ + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID"); + return -1; + } + + /* Remove zero-padding since some Registrar implementations seem to use + * hardcoded 32-octet length for this attribute */ + while (ssid_len > 0 && ssid[ssid_len - 1] == 0) + ssid_len--; + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len); + if (ssid_len <= sizeof(cred->ssid)) { + os_memcpy(cred->ssid, ssid, ssid_len); + cred->ssid_len = ssid_len; + } + + return 0; +} + + +static int wps_process_cred_auth_type(struct wps_credential *cred, + const u8 *auth_type) +{ + if (auth_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include " + "Authentication Type"); + return -1; + } + + cred->auth_type = WPA_GET_BE16(auth_type); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x", + cred->auth_type); + + return 0; +} + + +static int wps_process_cred_encr_type(struct wps_credential *cred, + const u8 *encr_type) +{ + if (encr_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include " + "Encryption Type"); + return -1; + } + + cred->encr_type = WPA_GET_BE16(encr_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x", + cred->encr_type); + + return 0; +} + + +static int wps_process_cred_network_key_idx(struct wps_credential *cred, + const u8 *key_idx) +{ + if (key_idx == NULL) + return 0; /* optional attribute */ + + wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx); + cred->key_idx = *key_idx; + + return 0; +} + + +static int wps_process_cred_network_key(struct wps_credential *cred, + const u8 *key, size_t key_len) +{ + if (key == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include " + "Network Key"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len); + if (key_len <= sizeof(cred->key)) { + os_memcpy(cred->key, key, key_len); + cred->key_len = key_len; + } + + return 0; +} + + +static int wps_process_cred_mac_addr(struct wps_credential *cred, + const u8 *mac_addr) +{ + if (mac_addr == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Credential did not include " + "MAC Address"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr)); + os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN); + + return 0; +} + + +static int wps_process_cred_eap_type(struct wps_credential *cred, + const u8 *eap_type, size_t eap_type_len) +{ + if (eap_type == NULL) + return 0; /* optional attribute */ + + wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len); + + return 0; +} + + +static int wps_process_cred_eap_identity(struct wps_credential *cred, + const u8 *identity, + size_t identity_len) +{ + if (identity == NULL) + return 0; /* optional attribute */ + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity", + identity, identity_len); + + return 0; +} + + +static int wps_process_cred_key_prov_auto(struct wps_credential *cred, + const u8 *key_prov_auto) +{ + if (key_prov_auto == NULL) + return 0; /* optional attribute */ + + wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d", + *key_prov_auto); + + return 0; +} + + +static int wps_process_cred_802_1x_enabled(struct wps_credential *cred, + const u8 *dot1x_enabled) +{ + if (dot1x_enabled == NULL) + return 0; /* optional attribute */ + + wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled); + + return 0; +} + + +static void wps_workaround_cred_key(struct wps_credential *cred) +{ + if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && + cred->key_len > 8 && cred->key_len < 64 && + cred->key[cred->key_len - 1] == 0) { + /* + * A deployed external registrar is known to encode ASCII + * passphrases incorrectly. Remove the extra NULL termination + * to fix the encoding. + */ + wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL " + "termination from ASCII passphrase"); + cred->key_len--; + } +} + + +int wps_process_cred(struct wps_parse_attr *attr, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: Process Credential"); + + /* TODO: support multiple Network Keys */ + if (wps_process_cred_network_idx(cred, attr->network_idx) || + wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) || + wps_process_cred_auth_type(cred, attr->auth_type) || + wps_process_cred_encr_type(cred, attr->encr_type) || + wps_process_cred_network_key_idx(cred, attr->network_key_idx) || + wps_process_cred_network_key(cred, attr->network_key, + attr->network_key_len) || + wps_process_cred_mac_addr(cred, attr->mac_addr) || + wps_process_cred_eap_type(cred, attr->eap_type, + attr->eap_type_len) || + wps_process_cred_eap_identity(cred, attr->eap_identity, + attr->eap_identity_len) || + wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) || + wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled)) + return -1; + + wps_workaround_cred_key(cred); + + return 0; +} + + +int wps_process_ap_settings(struct wps_parse_attr *attr, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings"); + os_memset(cred, 0, sizeof(*cred)); + /* TODO: optional attributes New Password and Device Password ID */ + if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) || + wps_process_cred_auth_type(cred, attr->auth_type) || + wps_process_cred_encr_type(cred, attr->encr_type) || + wps_process_cred_network_key_idx(cred, attr->network_key_idx) || + wps_process_cred_network_key(cred, attr->network_key, + attr->network_key_len) || + wps_process_cred_mac_addr(cred, attr->mac_addr)) + return -1; + + wps_workaround_cred_key(cred); + + return 0; +} diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c new file mode 100644 index 0000000000000..48af3032e77e2 --- /dev/null +++ b/src/wps/wps_common.c @@ -0,0 +1,337 @@ +/* + * Wi-Fi Protected Setup - common functionality + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "dh_groups.h" +#include "sha256.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "wps_i.h" +#include "wps_dev_attr.h" + + +void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, + const char *label, u8 *res, size_t res_len) +{ + u8 i_buf[4], key_bits[4]; + const u8 *addr[4]; + size_t len[4]; + int i, iter; + u8 hash[SHA256_MAC_LEN], *opos; + size_t left; + + WPA_PUT_BE32(key_bits, res_len * 8); + + addr[0] = i_buf; + len[0] = sizeof(i_buf); + addr[1] = label_prefix; + len[1] = label_prefix_len; + addr[2] = (const u8 *) label; + len[2] = os_strlen(label); + addr[3] = key_bits; + len[3] = sizeof(key_bits); + + iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN; + opos = res; + left = res_len; + + for (i = 1; i <= iter; i++) { + WPA_PUT_BE32(i_buf, i); + hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash); + if (i < iter) { + os_memcpy(opos, hash, SHA256_MAC_LEN); + opos += SHA256_MAC_LEN; + left -= SHA256_MAC_LEN; + } else + os_memcpy(opos, hash, left); + } +} + + +int wps_derive_keys(struct wps_data *wps) +{ + struct wpabuf *pubkey, *dh_shared; + u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN]; + + if (wps->dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available"); + return -1; + } + + pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r; + if (pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available"); + return -1; + } + + dh_shared = dh_derive_shared(pubkey, wps->dh_privkey, + dh_groups_get(WPS_DH_GROUP)); + dh_shared = wpabuf_zeropad(dh_shared, 192); + if (dh_shared == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key"); + return -1; + } + + /* Own DH private key is not needed anymore */ + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); + + /* DHKey = SHA-256(g^AB mod p) */ + addr[0] = wpabuf_head(dh_shared); + len[0] = wpabuf_len(dh_shared); + sha256_vector(1, addr, len, dhkey); + wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); + wpabuf_free(dh_shared); + + /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ + addr[0] = wps->nonce_e; + len[0] = WPS_NONCE_LEN; + addr[1] = wps->mac_addr_e; + len[1] = ETH_ALEN; + addr[2] = wps->nonce_r; + len[2] = WPS_NONCE_LEN; + hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk); + wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk)); + + wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation", + keys, sizeof(keys)); + os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN); + os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN); + os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN, + WPS_EMSK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey", + wps->authkey, WPS_AUTHKEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey", + wps->keywrapkey, WPS_KEYWRAPKEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN); + + return 0; +} + + +int wps_derive_mgmt_keys(struct wps_data *wps) +{ + u8 nonces[2 * WPS_NONCE_LEN]; + u8 keys[WPS_MGMTAUTHKEY_LEN + WPS_MGMTENCKEY_LEN]; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + const char *auth_label = "WFA-WLAN-Management-MgmtAuthKey"; + const char *enc_label = "WFA-WLAN-Management-MgmtEncKey"; + + /* MgmtAuthKey || MgmtEncKey = + * kdf(EMSK, N1 || N2 || "WFA-WLAN-Management-Keys", 384) */ + os_memcpy(nonces, wps->nonce_e, WPS_NONCE_LEN); + os_memcpy(nonces + WPS_NONCE_LEN, wps->nonce_r, WPS_NONCE_LEN); + wps_kdf(wps->emsk, nonces, sizeof(nonces), "WFA-WLAN-Management-Keys", + keys, sizeof(keys)); + os_memcpy(wps->mgmt_auth_key, keys, WPS_MGMTAUTHKEY_LEN); + os_memcpy(wps->mgmt_enc_key, keys + WPS_MGMTAUTHKEY_LEN, + WPS_MGMTENCKEY_LEN); + + addr[0] = nonces; + len[0] = sizeof(nonces); + + /* MgmtEncKeyID = first 128 bits of + * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtAuthKey") */ + addr[1] = (const u8 *) auth_label; + len[1] = os_strlen(auth_label); + sha256_vector(2, addr, len, hash); + os_memcpy(wps->mgmt_auth_key_id, hash, WPS_MGMT_KEY_ID_LEN); + + /* MgmtEncKeyID = first 128 bits of + * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtEncKey") */ + addr[1] = (const u8 *) enc_label; + len[1] = os_strlen(enc_label); + sha256_vector(2, addr, len, hash); + os_memcpy(wps->mgmt_enc_key_id, hash, WPS_MGMT_KEY_ID_LEN); + + wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtAuthKey", + wps->mgmt_auth_key, WPS_MGMTAUTHKEY_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: MgmtAuthKeyID", + wps->mgmt_auth_key_id, WPS_MGMT_KEY_ID_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtEncKey", + wps->mgmt_enc_key, WPS_MGMTENCKEY_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: MgmtEncKeyID", + wps->mgmt_enc_key_id, WPS_MGMT_KEY_ID_LEN); + + return 0; +} + + +void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len) +{ + u8 hash[SHA256_MAC_LEN]; + + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, + (dev_passwd_len + 1) / 2, hash); + os_memcpy(wps->psk1, hash, WPS_PSK_LEN); + hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, + dev_passwd + (dev_passwd_len + 1) / 2, + dev_passwd_len / 2, hash); + os_memcpy(wps->psk2, hash, WPS_PSK_LEN); + + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password", + dev_passwd, dev_passwd_len); + wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN); +} + + +struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, + size_t encr_len) +{ + struct wpabuf *decrypted; + const size_t block_size = 16; + size_t i; + u8 pad; + const u8 *pos; + + /* AES-128-CBC */ + if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size) + { + wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received"); + return NULL; + } + + decrypted = wpabuf_alloc(encr_len - block_size); + if (decrypted == NULL) + return NULL; + + wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len); + wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size); + if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted), + wpabuf_len(decrypted))) { + wpabuf_free(decrypted); + return NULL; + } + + wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings", + decrypted); + + pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1; + pad = *pos; + if (pad > wpabuf_len(decrypted)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value"); + wpabuf_free(decrypted); + return NULL; + } + for (i = 0; i < pad; i++) { + if (*pos-- != pad) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad " + "string"); + wpabuf_free(decrypted); + return NULL; + } + } + decrypted->used -= pad; + + return decrypted; +} + + +/** + * wps_pin_checksum - Compute PIN checksum + * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit) + * Returns: Checksum digit + */ +unsigned int wps_pin_checksum(unsigned int pin) +{ + unsigned int accum = 0; + while (pin) { + accum += 3 * (pin % 10); + pin /= 10; + accum += pin % 10; + pin /= 10; + } + + return (10 - accum % 10) % 10; +} + + +/** + * wps_pin_valid - Check whether a PIN has a valid checksum + * @pin: Eight digit PIN (i.e., including the checksum digit) + * Returns: 1 if checksum digit is valid, or 0 if not + */ +unsigned int wps_pin_valid(unsigned int pin) +{ + return wps_pin_checksum(pin / 10) == (pin % 10); +} + + +/** + * wps_generate_pin - Generate a random PIN + * Returns: Eight digit PIN (i.e., including the checksum digit) + */ +unsigned int wps_generate_pin(void) +{ + unsigned int val; + + /* Generate seven random digits for the PIN */ + if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) { + struct os_time now; + os_get_time(&now); + val = os_random() ^ now.sec ^ now.usec; + } + val %= 10000000; + + /* Append checksum digit */ + return val * 10 + wps_pin_checksum(val); +} + + +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg) +{ + union wps_event_data data; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + data.fail.msg = msg; + wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); +} + + +void wps_success_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL); +} + + +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part) +{ + union wps_event_data data; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + data.pwd_auth_fail.enrollee = enrollee; + data.pwd_auth_fail.part = part; + wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); +} diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h new file mode 100644 index 0000000000000..bf6ccc5f3ecba --- /dev/null +++ b/src/wps/wps_defs.h @@ -0,0 +1,297 @@ +/* + * Wi-Fi Protected Setup - message definitions + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_DEFS_H +#define WPS_DEFS_H + +#define WPS_VERSION 0x10 + +/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */ +#define WPS_DH_GROUP 5 + +#define WPS_UUID_LEN 16 +#define WPS_NONCE_LEN 16 +#define WPS_AUTHENTICATOR_LEN 8 +#define WPS_AUTHKEY_LEN 32 +#define WPS_KEYWRAPKEY_LEN 16 +#define WPS_EMSK_LEN 32 +#define WPS_PSK_LEN 16 +#define WPS_SECRET_NONCE_LEN 16 +#define WPS_HASH_LEN 32 +#define WPS_KWA_LEN 8 +#define WPS_MGMTAUTHKEY_LEN 32 +#define WPS_MGMTENCKEY_LEN 16 +#define WPS_MGMT_KEY_ID_LEN 16 + +/* Attribute Types */ +enum wps_attribute { + ATTR_AP_CHANNEL = 0x1001, + ATTR_ASSOC_STATE = 0x1002, + ATTR_AUTH_TYPE = 0x1003, + ATTR_AUTH_TYPE_FLAGS = 0x1004, + ATTR_AUTHENTICATOR = 0x1005, + ATTR_CONFIG_METHODS = 0x1008, + ATTR_CONFIG_ERROR = 0x1009, + ATTR_CONFIRM_URL4 = 0x100a, + ATTR_CONFIRM_URL6 = 0x100b, + ATTR_CONN_TYPE = 0x100c, + ATTR_CONN_TYPE_FLAGS = 0x100d, + ATTR_CRED = 0x100e, + ATTR_ENCR_TYPE = 0x100f, + ATTR_ENCR_TYPE_FLAGS = 0x1010, + ATTR_DEV_NAME = 0x1011, + ATTR_DEV_PASSWORD_ID = 0x1012, + ATTR_E_HASH1 = 0x1014, + ATTR_E_HASH2 = 0x1015, + ATTR_E_SNONCE1 = 0x1016, + ATTR_E_SNONCE2 = 0x1017, + ATTR_ENCR_SETTINGS = 0x1018, + ATTR_ENROLLEE_NONCE = 0x101a, + ATTR_FEATURE_ID = 0x101b, + ATTR_IDENTITY = 0x101c, + ATTR_IDENTITY_PROOF = 0x101d, + ATTR_KEY_WRAP_AUTH = 0x101e, + ATTR_KEY_ID = 0x101f, + ATTR_MAC_ADDR = 0x1020, + ATTR_MANUFACTURER = 0x1021, + ATTR_MSG_TYPE = 0x1022, + ATTR_MODEL_NAME = 0x1023, + ATTR_MODEL_NUMBER = 0x1024, + ATTR_NETWORK_INDEX = 0x1026, + ATTR_NETWORK_KEY = 0x1027, + ATTR_NETWORK_KEY_INDEX = 0x1028, + ATTR_NEW_DEVICE_NAME = 0x1029, + ATTR_NEW_PASSWORD = 0x102a, + ATTR_OOB_DEVICE_PASSWORD = 0x102c, + ATTR_OS_VERSION = 0x102d, + ATTR_POWER_LEVEL = 0x102f, + ATTR_PSK_CURRENT = 0x1030, + ATTR_PSK_MAX = 0x1031, + ATTR_PUBLIC_KEY = 0x1032, + ATTR_RADIO_ENABLE = 0x1033, + ATTR_REBOOT = 0x1034, + ATTR_REGISTRAR_CURRENT = 0x1035, + ATTR_REGISTRAR_ESTABLISHED = 0x1036, + ATTR_REGISTRAR_LIST = 0x1037, + ATTR_REGISTRAR_MAX = 0x1038, + ATTR_REGISTRAR_NONCE = 0x1039, + ATTR_REQUEST_TYPE = 0x103a, + ATTR_RESPONSE_TYPE = 0x103b, + ATTR_RF_BANDS = 0x103c, + ATTR_R_HASH1 = 0x103d, + ATTR_R_HASH2 = 0x103e, + ATTR_R_SNONCE1 = 0x103f, + ATTR_R_SNONCE2 = 0x1040, + ATTR_SELECTED_REGISTRAR = 0x1041, + ATTR_SERIAL_NUMBER = 0x1042, + ATTR_WPS_STATE = 0x1044, + ATTR_SSID = 0x1045, + ATTR_TOTAL_NETWORKS = 0x1046, + ATTR_UUID_E = 0x1047, + ATTR_UUID_R = 0x1048, + ATTR_VENDOR_EXT = 0x1049, + ATTR_VERSION = 0x104a, + ATTR_X509_CERT_REQ = 0x104b, + ATTR_X509_CERT = 0x104c, + ATTR_EAP_IDENTITY = 0x104d, + ATTR_MSG_COUNTER = 0x104e, + ATTR_PUBKEY_HASH = 0x104f, + ATTR_REKEY_KEY = 0x1050, + ATTR_KEY_LIFETIME = 0x1051, + ATTR_PERMITTED_CFG_METHODS = 0x1052, + ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053, + ATTR_PRIMARY_DEV_TYPE = 0x1054, + ATTR_SECONDARY_DEV_TYP_ELIST = 0x1055, + ATTR_PORTABLE_DEV = 0x1056, + ATTR_AP_SETUP_LOCKED = 0x1057, + ATTR_APPLICATION_EXT = 0x1058, + ATTR_EAP_TYPE = 0x1059, + ATTR_IV = 0x1060, + ATTR_KEY_PROVIDED_AUTO = 0x1061, + ATTR_802_1X_ENABLED = 0x1062, + ATTR_APPSESSIONKEY = 0x1063, + ATTR_WEPTRANSMITKEY = 0x1064 +}; + +/* Device Password ID */ +enum wps_dev_password_id { + DEV_PW_DEFAULT = 0x0000, + DEV_PW_USER_SPECIFIED = 0x0001, + DEV_PW_MACHINE_SPECIFIED = 0x0002, + DEV_PW_REKEY = 0x0003, + DEV_PW_PUSHBUTTON = 0x0004, + DEV_PW_REGISTRAR_SPECIFIED = 0x0005 +}; + +/* Message Type */ +enum wps_msg_type { + WPS_Beacon = 0x01, + WPS_ProbeRequest = 0x02, + WPS_ProbeResponse = 0x03, + WPS_M1 = 0x04, + WPS_M2 = 0x05, + WPS_M2D = 0x06, + WPS_M3 = 0x07, + WPS_M4 = 0x08, + WPS_M5 = 0x09, + WPS_M6 = 0x0a, + WPS_M7 = 0x0b, + WPS_M8 = 0x0c, + WPS_WSC_ACK = 0x0d, + WPS_WSC_NACK = 0x0e, + WPS_WSC_DONE = 0x0f +}; + +/* Authentication Type Flags */ +#define WPS_AUTH_OPEN 0x0001 +#define WPS_AUTH_WPAPSK 0x0002 +#define WPS_AUTH_SHARED 0x0004 +#define WPS_AUTH_WPA 0x0008 +#define WPS_AUTH_WPA2 0x0010 +#define WPS_AUTH_WPA2PSK 0x0020 +#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \ + WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK) + +/* Encryption Type Flags */ +#define WPS_ENCR_NONE 0x0001 +#define WPS_ENCR_WEP 0x0002 +#define WPS_ENCR_TKIP 0x0004 +#define WPS_ENCR_AES 0x0008 +#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \ + WPS_ENCR_AES) + +/* Configuration Error */ +enum wps_config_error { + WPS_CFG_NO_ERROR = 0, + WPS_CFG_OOB_IFACE_READ_ERROR = 1, + WPS_CFG_DECRYPTION_CRC_FAILURE = 2, + WPS_CFG_24_CHAN_NOT_SUPPORTED = 3, + WPS_CFG_50_CHAN_NOT_SUPPORTED = 4, + WPS_CFG_SIGNAL_TOO_WEAK = 5, + WPS_CFG_NETWORK_AUTH_FAILURE = 6, + WPS_CFG_NETWORK_ASSOC_FAILURE = 7, + WPS_CFG_NO_DHCP_RESPONSE = 8, + WPS_CFG_FAILED_DHCP_CONFIG = 9, + WPS_CFG_IP_ADDR_CONFLICT = 10, + WPS_CFG_NO_CONN_TO_REGISTRAR = 11, + WPS_CFG_MULTIPLE_PBC_DETECTED = 12, + WPS_CFG_ROGUE_SUSPECTED = 13, + WPS_CFG_DEVICE_BUSY = 14, + WPS_CFG_SETUP_LOCKED = 15, + WPS_CFG_MSG_TIMEOUT = 16, + WPS_CFG_REG_SESS_TIMEOUT = 17, + WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18 +}; + +/* RF Bands */ +#define WPS_RF_24GHZ 0x01 +#define WPS_RF_50GHZ 0x02 + +/* Config Methods */ +#define WPS_CONFIG_USBA 0x0001 +#define WPS_CONFIG_ETHERNET 0x0002 +#define WPS_CONFIG_LABEL 0x0004 +#define WPS_CONFIG_DISPLAY 0x0008 +#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 +#define WPS_CONFIG_INT_NFC_TOKEN 0x0020 +#define WPS_CONFIG_NFC_INTERFACE 0x0040 +#define WPS_CONFIG_PUSHBUTTON 0x0080 +#define WPS_CONFIG_KEYPAD 0x0100 + +/* Connection Type Flags */ +#define WPS_CONN_ESS 0x01 +#define WPS_CONN_IBSS 0x02 + +/* Wi-Fi Protected Setup State */ +enum wps_state { + WPS_STATE_NOT_CONFIGURED = 1, + WPS_STATE_CONFIGURED = 2 +}; + +/* Association State */ +enum wps_assoc_state { + WPS_ASSOC_NOT_ASSOC = 0, + WPS_ASSOC_CONN_SUCCESS = 1, + WPS_ASSOC_CFG_FAILURE = 2, + WPS_ASSOC_FAILURE = 3, + WPS_ASSOC_IP_FAILURE = 4 +}; + + +/* Primary Device Type */ +struct wps_dev_type { + u8 categ_id[2]; + u8 oui[4]; + u8 sub_categ_id[2]; +}; + +#define WPS_DEV_OUI_WFA 0x0050f204 + +enum wps_dev_categ { + WPS_DEV_COMPUTER = 1, + WPS_DEV_INPUT = 2, + WPS_DEV_PRINTER = 3, + WPS_DEV_CAMERA = 4, + WPS_DEV_STORAGE = 5, + WPS_DEV_NETWORK_INFRA = 6, + WPS_DEV_DISPLAY = 7, + WPS_DEV_MULTIMEDIA = 8, + WPS_DEV_GAMING = 9, + WPS_DEV_PHONE = 10 +}; + +enum wps_dev_subcateg { + WPS_DEV_COMPUTER_PC = 1, + WPS_DEV_COMPUTER_SERVER = 2, + WPS_DEV_COMPUTER_MEDIA_CENTER = 3, + WPS_DEV_PRINTER_PRINTER = 1, + WPS_DEV_PRINTER_SCANNER = 2, + WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1, + WPS_DEV_STORAGE_NAS = 1, + WPS_DEV_NETWORK_INFRA_AP = 1, + WPS_DEV_NETWORK_INFRA_ROUTER = 2, + WPS_DEV_NETWORK_INFRA_SWITCH = 3, + WPS_DEV_DISPLAY_TV = 1, + WPS_DEV_DISPLAY_PICTURE_FRAME = 2, + WPS_DEV_DISPLAY_PROJECTOR = 3, + WPS_DEV_MULTIMEDIA_DAR = 1, + WPS_DEV_MULTIMEDIA_PVR = 2, + WPS_DEV_MULTIMEDIA_MCX = 3, + WPS_DEV_GAMING_XBOX = 1, + WPS_DEV_GAMING_XBOX360 = 2, + WPS_DEV_GAMING_PLAYSTATION = 3, + WPS_DEV_PHONE_WINDOWS_MOBILE = 1 +}; + + +/* Request Type */ +enum wps_request_type { + WPS_REQ_ENROLLEE_INFO = 0, + WPS_REQ_ENROLLEE = 1, + WPS_REQ_REGISTRAR = 2, + WPS_REQ_WLAN_MANAGER_REGISTRAR = 3 +}; + +/* Response Type */ +enum wps_response_type { + WPS_RESP_ENROLLEE_INFO = 0, + WPS_RESP_ENROLLEE = 1, + WPS_RESP_REGISTRAR = 2, + WPS_RESP_AP = 3 +}; + +/* Walk Time for push button configuration (in seconds) */ +#define WPS_PBC_WALK_TIME 120 + +#endif /* WPS_DEFS_H */ diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c new file mode 100644 index 0000000000000..35f58d12d340d --- /dev/null +++ b/src/wps/wps_dev_attr.c @@ -0,0 +1,390 @@ +/* + * Wi-Fi Protected Setup - device attributes + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_i.h" +#include "wps_dev_attr.h" + + +static int wps_build_manufacturer(struct wps_device_data *dev, + struct wpabuf *msg) +{ + size_t len; + wpa_printf(MSG_DEBUG, "WPS: * Manufacturer"); + wpabuf_put_be16(msg, ATTR_MANUFACTURER); + len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0; + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zero-length + * attributes. As a workaround, send a null character if the + * device attribute string is empty. + */ + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, '\0'); + } else { + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->manufacturer, len); + } + return 0; +} + + +static int wps_build_model_name(struct wps_device_data *dev, + struct wpabuf *msg) +{ + size_t len; + wpa_printf(MSG_DEBUG, "WPS: * Model Name"); + wpabuf_put_be16(msg, ATTR_MODEL_NAME); + len = dev->model_name ? os_strlen(dev->model_name) : 0; + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zero-length + * attributes. As a workaround, send a null character if the + * device attribute string is empty. + */ + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, '\0'); + } else { + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_name, len); + } + return 0; +} + + +static int wps_build_model_number(struct wps_device_data *dev, + struct wpabuf *msg) +{ + size_t len; + wpa_printf(MSG_DEBUG, "WPS: * Model Number"); + wpabuf_put_be16(msg, ATTR_MODEL_NUMBER); + len = dev->model_number ? os_strlen(dev->model_number) : 0; + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zero-length + * attributes. As a workaround, send a null character if the + * device attribute string is empty. + */ + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, '\0'); + } else { + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_number, len); + } + return 0; +} + + +static int wps_build_serial_number(struct wps_device_data *dev, + struct wpabuf *msg) +{ + size_t len; + wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); + wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); + len = dev->serial_number ? os_strlen(dev->serial_number) : 0; + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zero-length + * attributes. As a workaround, send a null character if the + * device attribute string is empty. + */ + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, '\0'); + } else { + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->serial_number, len); + } + return 0; +} + + +int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) +{ + struct wps_dev_type *d; + wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type"); + wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE); + wpabuf_put_be16(msg, sizeof(*d)); + d = wpabuf_put(msg, sizeof(*d)); + WPA_PUT_BE16(d->categ_id, dev->categ); + WPA_PUT_BE32(d->oui, dev->oui); + WPA_PUT_BE16(d->sub_categ_id, dev->sub_categ); + return 0; +} + + +static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) +{ + size_t len; + wpa_printf(MSG_DEBUG, "WPS: * Device Name"); + wpabuf_put_be16(msg, ATTR_DEV_NAME); + len = dev->device_name ? os_strlen(dev->device_name) : 0; + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zero-length + * attributes. As a workaround, send a null character if the + * device attribute string is empty. + */ + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, '\0'); + } else { + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->device_name, len); + } + return 0; +} + + +int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg) +{ + if (wps_build_manufacturer(dev, msg) || + wps_build_model_name(dev, msg) || + wps_build_model_number(dev, msg) || + wps_build_serial_number(dev, msg) || + wps_build_primary_dev_type(dev, msg) || + wps_build_dev_name(dev, msg)) + return -1; + return 0; +} + + +int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * OS Version"); + wpabuf_put_be16(msg, ATTR_OS_VERSION); + wpabuf_put_be16(msg, 4); + wpabuf_put_be32(msg, 0x80000000 | dev->os_version); + return 0; +} + + +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); + wpabuf_put_be16(msg, ATTR_RF_BANDS); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, dev->rf_bands); + return 0; +} + + +static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, + size_t str_len) +{ + if (str == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); + + os_free(dev->manufacturer); + dev->manufacturer = os_malloc(str_len + 1); + if (dev->manufacturer == NULL) + return -1; + os_memcpy(dev->manufacturer, str, str_len); + dev->manufacturer[str_len] = '\0'; + + return 0; +} + + +static int wps_process_model_name(struct wps_device_data *dev, const u8 *str, + size_t str_len) +{ + if (str == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Model Name received"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); + + os_free(dev->model_name); + dev->model_name = os_malloc(str_len + 1); + if (dev->model_name == NULL) + return -1; + os_memcpy(dev->model_name, str, str_len); + dev->model_name[str_len] = '\0'; + + return 0; +} + + +static int wps_process_model_number(struct wps_device_data *dev, const u8 *str, + size_t str_len) +{ + if (str == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Model Number received"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); + + os_free(dev->model_number); + dev->model_number = os_malloc(str_len + 1); + if (dev->model_number == NULL) + return -1; + os_memcpy(dev->model_number, str, str_len); + dev->model_number[str_len] = '\0'; + + return 0; +} + + +static int wps_process_serial_number(struct wps_device_data *dev, + const u8 *str, size_t str_len) +{ + if (str == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Serial Number received"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); + + os_free(dev->serial_number); + dev->serial_number = os_malloc(str_len + 1); + if (dev->serial_number == NULL) + return -1; + os_memcpy(dev->serial_number, str, str_len); + dev->serial_number[str_len] = '\0'; + + return 0; +} + + +static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, + size_t str_len) +{ + if (str == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Name received"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); + + os_free(dev->device_name); + dev->device_name = os_malloc(str_len + 1); + if (dev->device_name == NULL) + return -1; + os_memcpy(dev->device_name, str, str_len); + dev->device_name[str_len] = '\0'; + + return 0; +} + + +static int wps_process_primary_dev_type(struct wps_device_data *dev, + const u8 *dev_type) +{ + struct wps_dev_type *d; + + if (dev_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received"); + return -1; + } + + d = (struct wps_dev_type *) dev_type; + dev->categ = WPA_GET_BE16(d->categ_id); + dev->oui = WPA_GET_BE32(d->oui); + dev->sub_categ = WPA_GET_BE16(d->sub_categ_id); + + wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: category %d " + "OUI %08x sub-category %d", + dev->categ, dev->oui, dev->sub_categ); + + return 0; +} + + +int wps_process_device_attrs(struct wps_device_data *dev, + struct wps_parse_attr *attr) +{ + if (wps_process_manufacturer(dev, attr->manufacturer, + attr->manufacturer_len) || + wps_process_model_name(dev, attr->model_name, + attr->model_name_len) || + wps_process_model_number(dev, attr->model_number, + attr->model_number_len) || + wps_process_serial_number(dev, attr->serial_number, + attr->serial_number_len) || + wps_process_primary_dev_type(dev, attr->primary_dev_type) || + wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len)) + return -1; + return 0; +} + + +int wps_process_os_version(struct wps_device_data *dev, const u8 *ver) +{ + if (ver == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No OS Version received"); + return -1; + } + + dev->os_version = WPA_GET_BE32(ver); + wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version); + + return 0; +} + + +int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) +{ + if (bands == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No RF Bands received"); + return -1; + } + + dev->rf_bands = *bands; + wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands); + + return 0; +} + + +void wps_device_data_dup(struct wps_device_data *dst, + const struct wps_device_data *src) +{ + if (src->device_name) + dst->device_name = os_strdup(src->device_name); + if (src->manufacturer) + dst->manufacturer = os_strdup(src->manufacturer); + if (src->model_name) + dst->model_name = os_strdup(src->model_name); + if (src->model_number) + dst->model_number = os_strdup(src->model_number); + if (src->serial_number) + dst->serial_number = os_strdup(src->serial_number); + dst->categ = src->categ; + dst->oui = src->oui; + dst->sub_categ = src->sub_categ; + dst->os_version = src->os_version; + dst->rf_bands = src->rf_bands; +} + + +void wps_device_data_free(struct wps_device_data *dev) +{ + os_free(dev->device_name); + dev->device_name = NULL; + os_free(dev->manufacturer); + dev->manufacturer = NULL; + os_free(dev->model_name); + dev->model_name = NULL; + os_free(dev->model_number); + dev->model_number = NULL; + os_free(dev->serial_number); + dev->serial_number = NULL; +} diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h new file mode 100644 index 0000000000000..a9c16ea22f2b7 --- /dev/null +++ b/src/wps/wps_dev_attr.h @@ -0,0 +1,33 @@ +/* + * Wi-Fi Protected Setup - device attributes + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_DEV_ATTR_H +#define WPS_DEV_ATTR_H + +struct wps_parse_attr; + +int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_primary_dev_type(struct wps_device_data *dev, + struct wpabuf *msg); +int wps_process_device_attrs(struct wps_device_data *dev, + struct wps_parse_attr *attr); +int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); +int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); +void wps_device_data_dup(struct wps_device_data *dst, + const struct wps_device_data *src); +void wps_device_data_free(struct wps_device_data *dev); + +#endif /* WPS_DEV_ATTR_H */ diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c new file mode 100644 index 0000000000000..179f7dbf2141a --- /dev/null +++ b/src/wps/wps_enrollee.c @@ -0,0 +1,1174 @@ +/* + * Wi-Fi Protected Setup - Enrollee + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "wps_i.h" +#include "wps_dev_attr.h" + + +static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * MAC Address"); + wpabuf_put_be16(msg, ATTR_MAC_ADDR); + wpabuf_put_be16(msg, ETH_ALEN); + wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN); + return 0; +} + + +static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) +{ + u8 state; + if (wps->wps->ap) + state = wps->wps->wps_state; + else + state = WPS_STATE_NOT_CONFIGURED; + wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)", + state); + wpabuf_put_be16(msg, ATTR_WPS_STATE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, WPS_STATE_NOT_CONFIGURED); + return 0; +} + + +static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg) +{ + u8 *hash; + const u8 *addr[4]; + size_t len[4]; + + if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: E-S2", + wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN); + + if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) { + wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for " + "E-Hash derivation"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: * E-Hash1"); + wpabuf_put_be16(msg, ATTR_E_HASH1); + wpabuf_put_be16(msg, SHA256_MAC_LEN); + hash = wpabuf_put(msg, SHA256_MAC_LEN); + /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */ + addr[0] = wps->snonce; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk1; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN); + + wpa_printf(MSG_DEBUG, "WPS: * E-Hash2"); + wpabuf_put_be16(msg, ATTR_E_HASH2); + wpabuf_put_be16(msg, SHA256_MAC_LEN); + hash = wpabuf_put(msg, SHA256_MAC_LEN); + /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */ + addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk2; + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN); + + return 0; +} + + +static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1"); + wpabuf_put_be16(msg, ATTR_E_SNONCE1); + wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); + wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN); + return 0; +} + + +static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2"); + wpabuf_put_be16(msg, ATTR_E_SNONCE2); + wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); + wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN, + WPS_SECRET_NONCE_LEN); + return 0; +} + + +static struct wpabuf * wps_build_m1(struct wps_data *wps) +{ + struct wpabuf *msg; + u16 methods; + + if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0) + return NULL; + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", + wps->nonce_e, WPS_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "WPS: Building Message M1"); + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + if (wps->pbc) + methods |= WPS_CONFIG_PUSHBUTTON; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M1) || + wps_build_uuid_e(msg, wps->uuid_e) || + wps_build_mac_addr(wps, msg) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_public_key(wps, msg) || + wps_build_auth_type_flags(wps, msg) || + wps_build_encr_type_flags(wps, msg) || + wps_build_conn_type_flags(wps, msg) || + wps_build_config_methods(msg, methods) || + wps_build_wps_state(wps, msg) || + wps_build_device_attrs(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_assoc_state(wps, msg) || + wps_build_dev_password_id(msg, wps->dev_pw_id) || + wps_build_config_error(msg, WPS_CFG_NO_ERROR) || + wps_build_os_version(&wps->wps->dev, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->state = RECV_M2; + return msg; +} + + +static struct wpabuf * wps_build_m3(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M3"); + + if (wps->dev_password == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password available"); + return NULL; + } + wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M3) || + wps_build_registrar_nonce(wps, msg) || + wps_build_e_hash(wps, msg) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->state = RECV_M4; + return msg; +} + + +static struct wpabuf * wps_build_m5(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M5"); + + plain = wpabuf_alloc(200); + if (plain == NULL) + return NULL; + + msg = wpabuf_alloc(1000); + if (msg == NULL) { + wpabuf_free(plain); + return NULL; + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M5) || + wps_build_registrar_nonce(wps, msg) || + wps_build_e_snonce1(wps, plain) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wps->state = RECV_M6; + return msg; +} + + +static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpabuf_put_be16(msg, ATTR_SSID); + wpabuf_put_be16(msg, wps->wps->ssid_len); + wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len); + return 0; +} + + +static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type"); + wpabuf_put_be16(msg, ATTR_AUTH_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, wps->wps->auth_types); + return 0; +} + + +static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type"); + wpabuf_put_be16(msg, ATTR_ENCR_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, wps->wps->encr_types); + return 0; +} + + +static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Network Key"); + wpabuf_put_be16(msg, ATTR_NETWORK_KEY); + wpabuf_put_be16(msg, wps->wps->network_key_len); + wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len); + return 0; +} + + +static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)"); + wpabuf_put_be16(msg, ATTR_MAC_ADDR); + wpabuf_put_be16(msg, ETH_ALEN); + wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN); + return 0; +} + + +static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain) +{ + if (wps->wps->ap_settings) { + wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)"); + wpabuf_put_data(plain, wps->wps->ap_settings, + wps->wps->ap_settings_len); + return 0; + } + + return wps_build_cred_ssid(wps, plain) || + wps_build_cred_mac_addr(wps, plain) || + wps_build_cred_auth_type(wps, plain) || + wps_build_cred_encr_type(wps, plain) || + wps_build_cred_network_key(wps, plain); +} + + +static struct wpabuf * wps_build_m7(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M7"); + + plain = wpabuf_alloc(500 + wps->wps->ap_settings_len); + if (plain == NULL) + return NULL; + + msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len); + if (msg == NULL) { + wpabuf_free(plain); + return NULL; + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M7) || + wps_build_registrar_nonce(wps, msg) || + wps_build_e_snonce2(wps, plain) || + (wps->wps->ap && wps_build_ap_settings(wps, plain)) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wps->state = RECV_M8; + return msg; +} + + +static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_DONE) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + if (wps->wps->ap) + wps->state = RECV_ACK; + else { + wps_success_event(wps->wps); + wps->state = WPS_FINISHED; + } + return msg; +} + + +static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_ACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_NACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_config_error(msg, wps->config_error)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, + enum wsc_op_code *op_code) +{ + struct wpabuf *msg; + + switch (wps->state) { + case SEND_M1: + msg = wps_build_m1(wps); + *op_code = WSC_MSG; + break; + case SEND_M3: + msg = wps_build_m3(wps); + *op_code = WSC_MSG; + break; + case SEND_M5: + msg = wps_build_m5(wps); + *op_code = WSC_MSG; + break; + case SEND_M7: + msg = wps_build_m7(wps); + *op_code = WSC_MSG; + break; + case RECEIVED_M2D: + if (wps->wps->ap) { + msg = wps_build_wsc_nack(wps); + *op_code = WSC_NACK; + break; + } + msg = wps_build_wsc_ack(wps); + *op_code = WSC_ACK; + if (msg) { + /* Another M2/M2D may be received */ + wps->state = RECV_M2; + } + break; + case SEND_WSC_NACK: + msg = wps_build_wsc_nack(wps); + *op_code = WSC_NACK; + break; + case WPS_MSG_DONE: + msg = wps_build_wsc_done(wps); + *op_code = WSC_Done; + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building " + "a message", wps->state); + msg = NULL; + break; + } + + if (*op_code == WSC_MSG && msg) { + /* Save a copy of the last message for Authenticator derivation + */ + wpabuf_free(wps->last_msg); + wps->last_msg = wpabuf_dup(msg); + } + + return msg; +} + + +static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce) +{ + if (r_nonce == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received"); + return -1; + } + + os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", + wps->nonce_r, WPS_NONCE_LEN); + + return 0; +} + + +static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce) +{ + if (e_nonce == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received"); + return -1; + } + + if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received"); + return -1; + } + + return 0; +} + + +static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r) +{ + if (uuid_r == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-R received"); + return -1; + } + + os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN); + + return 0; +} + + +static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, + size_t pk_len) +{ + if (pk == NULL || pk_len == 0) { + wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); + return -1; + } + + wpabuf_free(wps->dh_pubkey_r); + wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); + if (wps->dh_pubkey_r == NULL) + return -1; + + if (wps_derive_keys(wps) < 0) + return -1; + + if (wps->request_type == WPS_REQ_WLAN_MANAGER_REGISTRAR && + wps_derive_mgmt_keys(wps) < 0) + return -1; + + return 0; +} + + +static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1) +{ + if (r_hash1 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received"); + return -1; + } + + os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN); + + return 0; +} + + +static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2) +{ + if (r_hash2 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received"); + return -1; + } + + os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN); + + return 0; +} + + +static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + + if (r_snonce1 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1, + WPS_SECRET_NONCE_LEN); + + /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */ + addr[0] = r_snonce1; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk1; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + + if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does " + "not match with the pre-committed value"); + wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; + wps_pwd_auth_fail_event(wps->wps, 1, 1); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first " + "half of the device password"); + + return 0; +} + + +static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + + if (r_snonce2 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2, + WPS_SECRET_NONCE_LEN); + + /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */ + addr[0] = r_snonce2; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk2; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + + if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does " + "not match with the pre-committed value"); + wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; + wps_pwd_auth_fail_event(wps->wps, 1, 2); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second " + "half of the device password"); + + return 0; +} + + +static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, + size_t cred_len) +{ + struct wps_parse_attr attr; + struct wpabuf msg; + + wpa_printf(MSG_DEBUG, "WPS: Received Credential"); + os_memset(&wps->cred, 0, sizeof(wps->cred)); + wpabuf_set(&msg, cred, cred_len); + if (wps_parse_msg(&msg, &attr) < 0 || + wps_process_cred(&attr, &wps->cred)) + return -1; + + if (wps->wps->cred_cb) { + wps->cred.cred_attr = cred - 4; + wps->cred.cred_attr_len = cred_len + 4; + wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + wps->cred.cred_attr = NULL; + wps->cred.cred_attr_len = 0; + } + + return 0; +} + + +static int wps_process_creds(struct wps_data *wps, const u8 *cred[], + size_t cred_len[], size_t num_cred) +{ + size_t i; + + if (wps->wps->ap) + return 0; + + if (num_cred == 0) { + wpa_printf(MSG_DEBUG, "WPS: No Credential attributes " + "received"); + return -1; + } + + for (i = 0; i < num_cred; i++) { + if (wps_process_cred_e(wps, cred[i], cred_len[i])) + return -1; + } + + return 0; +} + + +static int wps_process_ap_settings_e(struct wps_data *wps, + struct wps_parse_attr *attr, + struct wpabuf *attrs) +{ + struct wps_credential cred; + + if (!wps->wps->ap) + return 0; + + if (wps_process_ap_settings(attr, &cred) < 0) + return -1; + + wpa_printf(MSG_INFO, "WPS: Received new AP configuration from " + "Registrar"); + + if (wps->wps->cred_cb) { + cred.cred_attr = wpabuf_head(attrs); + cred.cred_attr_len = wpabuf_len(attrs); + wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + } + + return 0; +} + + +static enum wps_process_res wps_process_m2(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + wpa_printf(MSG_DEBUG, "WPS: Received M2"); + + if (wps->state != RECV_M2) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M2", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || + wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || + wps_process_uuid_r(wps, attr->uuid_r) || + wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || + wps_process_authenticator(wps, attr->authenticator, msg)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps->wps->ap && wps->wps->ap_setup_locked) { + wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " + "registration of a new Registrar"); + wps->config_error = WPS_CFG_SETUP_LOCKED; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wps->state = SEND_M3; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m2d(struct wps_data *wps, + struct wps_parse_attr *attr) +{ + wpa_printf(MSG_DEBUG, "WPS: Received M2D"); + + if (wps->state != RECV_M2) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M2D", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", + attr->manufacturer, attr->manufacturer_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", + attr->model_name, attr->model_name_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", + attr->model_number, attr->model_number_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", + attr->serial_number, attr->serial_number_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", + attr->dev_name, attr->dev_name_len); + + if (wps->wps->event_cb) { + union wps_event_data data; + struct wps_event_m2d *m2d = &data.m2d; + os_memset(&data, 0, sizeof(data)); + if (attr->config_methods) + m2d->config_methods = + WPA_GET_BE16(attr->config_methods); + m2d->manufacturer = attr->manufacturer; + m2d->manufacturer_len = attr->manufacturer_len; + m2d->model_name = attr->model_name; + m2d->model_name_len = attr->model_name_len; + m2d->model_number = attr->model_number; + m2d->model_number_len = attr->model_number_len; + m2d->serial_number = attr->serial_number; + m2d->serial_number_len = attr->serial_number_len; + m2d->dev_name = attr->dev_name; + m2d->dev_name_len = attr->dev_name_len; + m2d->primary_dev_type = attr->primary_dev_type; + if (attr->config_error) + m2d->config_error = + WPA_GET_BE16(attr->config_error); + if (attr->dev_password_id) + m2d->dev_password_id = + WPA_GET_BE16(attr->dev_password_id); + wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data); + } + + wps->state = RECEIVED_M2D; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m4(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + wpa_printf(MSG_DEBUG, "WPS: Received M4"); + + if (wps->state != RECV_M4) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M4", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg) || + wps_process_r_hash1(wps, attr->r_hash1) || + wps_process_r_hash2(wps, attr->r_hash2)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + "Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " + "attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || + wps_process_r_snonce1(wps, eattr.r_snonce1)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = SEND_M5; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m6(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + wpa_printf(MSG_DEBUG, "WPS: Received M6"); + + if (wps->state != RECV_M6) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M6", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + "Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " + "attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || + wps_process_r_snonce2(wps, eattr.r_snonce2)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = SEND_M7; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m8(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + wpa_printf(MSG_DEBUG, "WPS: Received M8"); + + if (wps->state != RECV_M8) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M8", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + "Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " + "attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || + wps_process_creds(wps, eattr.cred, eattr.cred_len, + eattr.num_cred) || + wps_process_ap_settings_e(wps, &eattr, decrypted)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = WPS_MSG_DONE; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + enum wps_process_res ret = WPS_CONTINUE; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG"); + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + switch (*attr.msg_type) { + case WPS_M2: + ret = wps_process_m2(wps, msg, &attr); + break; + case WPS_M2D: + ret = wps_process_m2d(wps, &attr); + break; + case WPS_M4: + ret = wps_process_m4(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M4); + break; + case WPS_M6: + ret = wps_process_m6(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M6); + break; + case WPS_M8: + ret = wps_process_m8(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M8); + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + + /* + * Save a copy of the last message for Authenticator derivation if we + * are continuing. However, skip M2D since it is not authenticated and + * neither is the ACK/NACK response frame. This allows the possibly + * following M2 to be processed correctly by using the previously sent + * M1 in Authenticator derivation. + */ + if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) { + /* Save a copy of the last message for Authenticator derivation + */ + wpabuf_free(wps->last_msg); + wps->last_msg = wpabuf_dup(msg); + } + + return ret; +} + + +static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK"); + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_WSC_ACK) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + + if (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + return WPS_FAILURE; + } + + if (wps->state == RECV_ACK && wps->wps->ap) { + wpa_printf(MSG_DEBUG, "WPS: External Registrar registration " + "completed successfully"); + wps_success_event(wps->wps); + wps->state = WPS_FINISHED; + return WPS_DONE; + } + + return WPS_FAILURE; +} + + +static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_WSC_NACK) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + + if (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", + attr.registrar_nonce, WPS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce", + wps->nonce_r, WPS_NONCE_LEN); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", + attr.enrollee_nonce, WPS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce", + wps->nonce_e, WPS_NONCE_LEN); + return WPS_FAILURE; + } + + if (attr.config_error == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute " + "in WSC_NACK"); + return WPS_FAILURE; + } + + wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with " + "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + + switch (wps->state) { + case RECV_M4: + wps_fail_event(wps->wps, WPS_M3); + break; + case RECV_M6: + wps_fail_event(wps->wps, WPS_M5); + break; + case RECV_M8: + wps_fail_event(wps->wps, WPS_M7); + break; + default: + break; + } + + /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if + * Enrollee is Authenticator */ + wps->state = SEND_WSC_NACK; + + return WPS_FAILURE; +} + + +enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg) +{ + + wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu " + "op_code=%d)", + (unsigned long) wpabuf_len(msg), op_code); + + switch (op_code) { + case WSC_MSG: + case WSC_UPnP: + return wps_process_wsc_msg(wps, msg); + case WSC_ACK: + return wps_process_wsc_ack(wps, msg); + case WSC_NACK: + return wps_process_wsc_nack(wps, msg); + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); + return WPS_FAILURE; + } +} diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h new file mode 100644 index 0000000000000..85adf28d4f62d --- /dev/null +++ b/src/wps/wps_i.h @@ -0,0 +1,248 @@ +/* + * Wi-Fi Protected Setup - internal definitions + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_I_H +#define WPS_I_H + +#include "wps.h" + +/** + * struct wps_data - WPS registration protocol data + * + * This data is stored at the EAP-WSC server/peer method and it is kept for a + * single registration protocol run. + */ +struct wps_data { + /** + * wps - Pointer to long term WPS context + */ + struct wps_context *wps; + + /** + * registrar - Whether this end is a Registrar + */ + int registrar; + + enum { + /* Enrollee states */ + SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7, + RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED, + SEND_WSC_NACK, + + /* Registrar states */ + RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6, + RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK + } state; + + u8 uuid_e[WPS_UUID_LEN]; + u8 uuid_r[WPS_UUID_LEN]; + u8 mac_addr_e[ETH_ALEN]; + u8 nonce_e[WPS_NONCE_LEN]; + u8 nonce_r[WPS_NONCE_LEN]; + u8 psk1[WPS_PSK_LEN]; + u8 psk2[WPS_PSK_LEN]; + u8 snonce[2 * WPS_SECRET_NONCE_LEN]; + u8 peer_hash1[WPS_HASH_LEN]; + u8 peer_hash2[WPS_HASH_LEN]; + + struct wpabuf *dh_privkey; + struct wpabuf *dh_pubkey_e; + struct wpabuf *dh_pubkey_r; + u8 authkey[WPS_AUTHKEY_LEN]; + u8 keywrapkey[WPS_KEYWRAPKEY_LEN]; + u8 emsk[WPS_EMSK_LEN]; + u8 mgmt_auth_key[WPS_MGMTAUTHKEY_LEN]; + u8 mgmt_auth_key_id[WPS_MGMT_KEY_ID_LEN]; + u8 mgmt_enc_key[WPS_MGMTENCKEY_LEN]; + u8 mgmt_enc_key_id[WPS_MGMT_KEY_ID_LEN]; + + struct wpabuf *last_msg; + + u8 *dev_password; + size_t dev_password_len; + u16 dev_pw_id; + int pbc; + + /** + * request_type - Request Type attribute from (Re)AssocReq + */ + u8 request_type; + + /** + * encr_type - Available encryption types + */ + u16 encr_type; + + /** + * auth_type - Available authentication types + */ + u16 auth_type; + + u8 *new_psk; + size_t new_psk_len; + + int wps_pin_revealed; + struct wps_credential cred; + + struct wps_device_data peer_dev; + + /** + * config_error - Configuration Error value to be used in NACK + */ + u16 config_error; + + int ext_reg; +}; + + +struct wps_parse_attr { + /* fixed length fields */ + const u8 *version; /* 1 octet */ + const u8 *msg_type; /* 1 octet */ + const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ + const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ + const u8 *auth_type_flags; /* 2 octets */ + const u8 *encr_type_flags; /* 2 octets */ + const u8 *conn_type_flags; /* 1 octet */ + const u8 *config_methods; /* 2 octets */ + const u8 *sel_reg_config_methods; /* 2 octets */ + const u8 *primary_dev_type; /* 8 octets */ + const u8 *rf_bands; /* 1 octet */ + const u8 *assoc_state; /* 2 octets */ + const u8 *config_error; /* 2 octets */ + const u8 *dev_password_id; /* 2 octets */ + const u8 *os_version; /* 4 octets */ + const u8 *wps_state; /* 1 octet */ + const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ + const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ + const u8 *auth_type; /* 2 octets */ + const u8 *encr_type; /* 2 octets */ + const u8 *network_idx; /* 1 octet */ + const u8 *network_key_idx; /* 1 octet */ + const u8 *mac_addr; /* ETH_ALEN (6) octets */ + const u8 *key_prov_auto; /* 1 octet (Bool) */ + const u8 *dot1x_enabled; /* 1 octet (Bool) */ + const u8 *selected_registrar; /* 1 octet (Bool) */ + const u8 *request_type; /* 1 octet */ + const u8 *response_type; /* 1 octet */ + + /* variable length fields */ + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + const u8 *dev_name; + size_t dev_name_len; + const u8 *public_key; + size_t public_key_len; + const u8 *encr_settings; + size_t encr_settings_len; + const u8 *ssid; /* <= 32 octets */ + size_t ssid_len; + const u8 *network_key; /* <= 64 octets */ + size_t network_key_len; + const u8 *eap_type; /* <= 8 octets */ + size_t eap_type_len; + const u8 *eap_identity; /* <= 64 octets */ + size_t eap_identity_len; + + /* attributes that can occur multiple times */ +#define MAX_CRED_COUNT 10 + const u8 *cred[MAX_CRED_COUNT]; + size_t cred_len[MAX_CRED_COUNT]; + size_t num_cred; +}; + +/* wps_common.c */ +void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, + const char *label, u8 *res, size_t res_len); +int wps_derive_keys(struct wps_data *wps); +int wps_derive_mgmt_keys(struct wps_data *wps); +void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len); +struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, + size_t encr_len); +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg); +void wps_success_event(struct wps_context *wps); +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); + +/* wps_attr_parse.c */ +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); + +/* wps_attr_build.c */ +int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); +int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type); +int wps_build_config_methods(struct wpabuf *msg, u16 methods); +int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid); +int wps_build_dev_password_id(struct wpabuf *msg, u16 id); +int wps_build_config_error(struct wpabuf *msg, u16 err); +int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg); +int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg); +int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, + struct wpabuf *plain); +int wps_build_version(struct wpabuf *msg); +int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); +int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); +int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); +int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg); +int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); +int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); +int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); + +/* wps_attr_process.c */ +int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, + const struct wpabuf *msg); +int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg, + const u8 *key_wrap_auth); +int wps_process_cred(struct wps_parse_attr *attr, + struct wps_credential *cred); +int wps_process_ap_settings(struct wps_parse_attr *attr, + struct wps_credential *cred); + +/* wps_enrollee.c */ +struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, + enum wsc_op_code *op_code); +enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg); + +/* wps_registrar.c */ +struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, + enum wsc_op_code *op_code); +enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg); + + +static inline int wps_version_supported(const u8 *version) +{ + /* Require major version match, but allow minor version differences */ + return version && (*version & 0xf0) == (WPS_VERSION & 0xf0); +} + +#endif /* WPS_I_H */ diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c new file mode 100644 index 0000000000000..f7eebddd63bc4 --- /dev/null +++ b/src/wps/wps_registrar.c @@ -0,0 +1,2448 @@ +/* + * Wi-Fi Protected Setup - Registrar + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "base64.h" +#include "ieee802_11_defs.h" +#include "eloop.h" +#include "wps_i.h" +#include "wps_dev_attr.h" +#include "wps_upnp.h" + + +struct wps_uuid_pin { + struct wps_uuid_pin *next; + u8 uuid[WPS_UUID_LEN]; + int wildcard_uuid; + u8 *pin; + size_t pin_len; + int locked; +}; + + +static void wps_free_pin(struct wps_uuid_pin *pin) +{ + os_free(pin->pin); + os_free(pin); +} + + +static void wps_free_pins(struct wps_uuid_pin *pins) +{ + struct wps_uuid_pin *pin, *prev; + + pin = pins; + while (pin) { + prev = pin; + pin = pin->next; + wps_free_pin(prev); + } +} + + +struct wps_pbc_session { + struct wps_pbc_session *next; + u8 addr[ETH_ALEN]; + u8 uuid_e[WPS_UUID_LEN]; + struct os_time timestamp; +}; + + +static void wps_free_pbc_sessions(struct wps_pbc_session *pbc) +{ + struct wps_pbc_session *prev; + + while (pbc) { + prev = pbc; + pbc = pbc->next; + os_free(prev); + } +} + + +struct wps_registrar { + struct wps_context *wps; + + int pbc; + int selected_registrar; + + int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len); + int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len, + const u8 *probe_resp_ie, size_t probe_resp_ie_len); + void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev); + void (*reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e); + void *cb_ctx; + + struct wps_uuid_pin *pins; + struct wps_pbc_session *pbc_sessions; + + int skip_cred_build; + struct wpabuf *extra_cred; + int disable_auto_conf; + int sel_reg_dev_password_id_override; + int sel_reg_config_methods_override; +}; + + +static int wps_set_ie(struct wps_registrar *reg); +static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_registrar_set_selected_timeout(void *eloop_ctx, + void *timeout_ctx); + + +static void wps_registrar_add_pbc_session(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e) +{ + struct wps_pbc_session *pbc, *prev = NULL; + struct os_time now; + + os_get_time(&now); + + pbc = reg->pbc_sessions; + while (pbc) { + if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 && + os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) { + if (prev) + prev->next = pbc->next; + else + reg->pbc_sessions = pbc->next; + break; + } + prev = pbc; + pbc = pbc->next; + } + + if (!pbc) { + pbc = os_zalloc(sizeof(*pbc)); + if (pbc == NULL) + return; + os_memcpy(pbc->addr, addr, ETH_ALEN); + if (uuid_e) + os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN); + } + + pbc->next = reg->pbc_sessions; + reg->pbc_sessions = pbc; + pbc->timestamp = now; + + /* remove entries that have timed out */ + prev = pbc; + pbc = pbc->next; + + while (pbc) { + if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { + prev->next = NULL; + wps_free_pbc_sessions(pbc); + break; + } + prev = pbc; + pbc = pbc->next; + } +} + + +static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e) +{ + struct wps_pbc_session *pbc, *prev = NULL; + + pbc = reg->pbc_sessions; + while (pbc) { + if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 && + os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) { + if (prev) + prev->next = pbc->next; + else + reg->pbc_sessions = pbc->next; + os_free(pbc); + break; + } + prev = pbc; + pbc = pbc->next; + } +} + + +static int wps_registrar_pbc_overlap(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e) +{ + int count = 0; + struct wps_pbc_session *pbc; + struct os_time now; + + os_get_time(&now); + + for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) { + if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) + break; + if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) || + uuid_e == NULL || + os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) + count++; + } + + if (addr || uuid_e) + count++; + + return count > 1 ? 1 : 0; +} + + +static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)", + wps->wps_state); + wpabuf_put_be16(msg, ATTR_WPS_STATE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, wps->wps_state); + return 0; +} + + +#ifdef CONFIG_WPS_UPNP +static void wps_registrar_free_pending_m2(struct wps_context *wps) +{ + struct upnp_pending_message *p, *p2, *prev = NULL; + p = wps->upnp_msgs; + while (p) { + if (p->type == WPS_M2 || p->type == WPS_M2D) { + if (prev == NULL) + wps->upnp_msgs = p->next; + else + prev->next = p->next; + wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D"); + p2 = p; + p = p->next; + wpabuf_free(p2->msg); + os_free(p2); + continue; + } + prev = p; + p = p->next; + } +} +#endif /* CONFIG_WPS_UPNP */ + + +static int wps_build_ap_setup_locked(struct wps_context *wps, + struct wpabuf *msg) +{ + if (wps->ap_setup_locked) { + wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked"); + wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 1); + } + return 0; +} + + +static int wps_build_selected_registrar(struct wps_registrar *reg, + struct wpabuf *msg) +{ + if (!reg->selected_registrar) + return 0; + wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar"); + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 1); + return 0; +} + + +static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; + if (!reg->selected_registrar) + return 0; + if (reg->sel_reg_dev_password_id_override >= 0) + id = reg->sel_reg_dev_password_id_override; + wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id); + wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, id); + return 0; +} + + +static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 methods; + if (!reg->selected_registrar) + return 0; + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) + methods |= WPS_CONFIG_PUSHBUTTON; + if (reg->sel_reg_config_methods_override >= 0) + methods = reg->sel_reg_config_methods_override; + wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)", + methods); + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, methods); + return 0; +} + + +static int wps_build_probe_config_methods(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 methods; + methods = 0; + wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); + wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, methods); + return 0; +} + + +static int wps_build_config_methods_r(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 methods; + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) + methods |= WPS_CONFIG_PUSHBUTTON; + return wps_build_config_methods(msg, methods); +} + + +static int wps_build_resp_type(struct wps_registrar *reg, struct wpabuf *msg) +{ + u8 resp = reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR; + wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", resp); + wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, resp); + return 0; +} + + +/** + * wps_registrar_init - Initialize WPS Registrar data + * @wps: Pointer to longterm WPS context + * @cfg: Registrar configuration + * Returns: Pointer to allocated Registrar data or %NULL on failure + * + * This function is used to initialize WPS Registrar functionality. It can be + * used for a single Registrar run (e.g., when run in a supplicant) or multiple + * runs (e.g., when run as an internal Registrar in an AP). Caller is + * responsible for freeing the returned data with wps_registrar_deinit() when + * Registrar functionality is not needed anymore. + */ +struct wps_registrar * +wps_registrar_init(struct wps_context *wps, + const struct wps_registrar_config *cfg) +{ + struct wps_registrar *reg = os_zalloc(sizeof(*reg)); + if (reg == NULL) + return NULL; + + reg->wps = wps; + reg->new_psk_cb = cfg->new_psk_cb; + reg->set_ie_cb = cfg->set_ie_cb; + reg->pin_needed_cb = cfg->pin_needed_cb; + reg->reg_success_cb = cfg->reg_success_cb; + reg->cb_ctx = cfg->cb_ctx; + reg->skip_cred_build = cfg->skip_cred_build; + if (cfg->extra_cred) { + reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred, + cfg->extra_cred_len); + if (reg->extra_cred == NULL) { + os_free(reg); + return NULL; + } + } + reg->disable_auto_conf = cfg->disable_auto_conf; + reg->sel_reg_dev_password_id_override = -1; + reg->sel_reg_config_methods_override = -1; + + if (wps_set_ie(reg)) { + wps_registrar_deinit(reg); + return NULL; + } + + return reg; +} + + +/** + * wps_registrar_deinit - Deinitialize WPS Registrar data + * @reg: Registrar data from wps_registrar_init() + */ +void wps_registrar_deinit(struct wps_registrar *reg) +{ + if (reg == NULL) + return; + eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + wps_free_pins(reg->pins); + wps_free_pbc_sessions(reg->pbc_sessions); + wpabuf_free(reg->extra_cred); + os_free(reg); +} + + +/** + * wps_registrar_add_pin - Configure a new PIN for Registrar + * @reg: Registrar data from wps_registrar_init() + * @uuid: UUID-E or %NULL for wildcard (any UUID) + * @pin: PIN (Device Password) + * @pin_len: Length of pin in octets + * Returns: 0 on success, -1 on failure + */ +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, + const u8 *pin, size_t pin_len) +{ + struct wps_uuid_pin *p; + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + if (uuid == NULL) + p->wildcard_uuid = 1; + else + os_memcpy(p->uuid, uuid, WPS_UUID_LEN); + p->pin = os_malloc(pin_len); + if (p->pin == NULL) { + os_free(p); + return -1; + } + os_memcpy(p->pin, pin, pin_len); + p->pin_len = pin_len; + + p->next = reg->pins; + reg->pins = p; + + wpa_printf(MSG_DEBUG, "WPS: A new PIN configured"); + wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN); + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); + reg->selected_registrar = 1; + reg->pbc = 0; + wps_set_ie(reg); + + return 0; +} + + +/** + * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E + * @reg: Registrar data from wps_registrar_init() + * @uuid: UUID-E + * Returns: 0 on success, -1 on failure (e.g., PIN not found) + */ +int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) +{ + struct wps_uuid_pin *pin, *prev; + + prev = NULL; + pin = reg->pins; + while (pin) { + if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { + if (prev == NULL) + reg->pins = pin->next; + else + prev->next = pin->next; + wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", + pin->uuid, WPS_UUID_LEN); + wps_free_pin(pin); + return 0; + } + prev = pin; + pin = pin->next; + } + + return -1; +} + + +static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, + const u8 *uuid, size_t *pin_len) +{ + struct wps_uuid_pin *pin; + + pin = reg->pins; + while (pin) { + if (!pin->wildcard_uuid && + os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) + break; + pin = pin->next; + } + + if (!pin) { + /* Check for wildcard UUIDs since none of the UUID-specific + * PINs matched */ + pin = reg->pins; + while (pin) { + if (pin->wildcard_uuid == 1) { + wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " + "PIN. Assigned it for this UUID-E"); + pin->wildcard_uuid = 2; + os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); + break; + } + pin = pin->next; + } + } + + if (!pin) + return NULL; + + /* + * Lock the PIN to avoid attacks based on concurrent re-use of the PIN + * that could otherwise avoid PIN invalidations. + */ + if (pin->locked) { + wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " + "allow concurrent re-use"); + return NULL; + } + *pin_len = pin->pin_len; + pin->locked = 1; + return pin->pin; +} + + +/** + * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E + * @reg: Registrar data from wps_registrar_init() + * @uuid: UUID-E + * Returns: 0 on success, -1 on failure + * + * PINs are locked to enforce only one concurrent use. This function unlocks a + * PIN to allow it to be used again. If the specified PIN was configured using + * a wildcard UUID, it will be removed instead of allowing multiple uses. + */ +int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) +{ + struct wps_uuid_pin *pin; + + pin = reg->pins; + while (pin) { + if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { + if (pin->wildcard_uuid == 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalidating used " + "wildcard PIN"); + return wps_registrar_invalidate_pin(reg, uuid); + } + pin->locked = 0; + return 0; + } + pin = pin->next; + } + + return -1; +} + + +static void wps_registrar_stop_pbc(struct wps_registrar *reg) +{ + reg->selected_registrar = 0; + reg->pbc = 0; + wps_set_ie(reg); +} + + +static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wps_registrar *reg = eloop_ctx; + + wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode"); + wps_registrar_stop_pbc(reg); +} + + +/** + * wps_registrar_button_pushed - Notify Registrar that AP button was pushed + * @reg: Registrar data from wps_registrar_init() + * Returns: 0 on success, -1 on failure + * + * This function is called on an AP when a push button is pushed to activate + * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout + * or when a PBC registration is completed. + */ +int wps_registrar_button_pushed(struct wps_registrar *reg) +{ + if (wps_registrar_pbc_overlap(reg, NULL, NULL)) { + wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " + "mode"); + return -1; + } + wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); + reg->selected_registrar = 1; + reg->pbc = 1; + wps_set_ie(reg); + + eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, + reg, NULL); + return 0; +} + + +static void wps_registrar_pbc_completed(struct wps_registrar *reg) +{ + wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode"); + eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); + wps_registrar_stop_pbc(reg); +} + + +/** + * wps_registrar_probe_req_rx - Notify Registrar of Probe Request + * @reg: Registrar data from wps_registrar_init() + * @addr: MAC address of the Probe Request sender + * @wps_data: WPS IE contents + * + * This function is called on an AP when a Probe Request with WPS IE is + * received. This is used to track PBC mode use and to detect possible overlap + * situation with other WPS APs. + */ +void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, + const struct wpabuf *wps_data) +{ + struct wps_parse_attr attr; + u16 methods; + + wpa_hexdump_buf(MSG_MSGDUMP, + "WPS: Probe Request with WPS data received", + wps_data); + + if (wps_parse_msg(wps_data, &attr) < 0) + return; + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE " + "version 0x%x", attr.version ? *attr.version : 0); + return; + } + + if (attr.config_methods == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in " + "Probe Request"); + return; + } + + methods = WPA_GET_BE16(attr.config_methods); + if (!(methods & WPS_CONFIG_PUSHBUTTON)) + return; /* Not PBC */ + + wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from " + MACSTR, MAC2STR(addr)); + + wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); +} + + +static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, + const u8 *psk, size_t psk_len) +{ + if (reg->new_psk_cb == NULL) + return 0; + + return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len); +} + + +static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + if (reg->pin_needed_cb == NULL) + return; + + reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev); +} + + +static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, + const u8 *uuid_e) +{ + if (reg->reg_success_cb == NULL) + return; + + reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e); +} + + +static int wps_cb_set_ie(struct wps_registrar *reg, + const struct wpabuf *beacon_ie, + const struct wpabuf *probe_resp_ie) +{ + if (reg->set_ie_cb == NULL) + return 0; + + return reg->set_ie_cb(reg->cb_ctx, wpabuf_head(beacon_ie), + wpabuf_len(beacon_ie), + wpabuf_head(probe_resp_ie), + wpabuf_len(probe_resp_ie)); +} + + +/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ +static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + ie = wpabuf_alloc(wpabuf_len(data) + 100); + if (ie == NULL) { + wpabuf_free(data); + return NULL; + } + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + wpabuf_free(data); + + return ie; +} + + +static int wps_set_ie(struct wps_registrar *reg) +{ + struct wpabuf *beacon; + struct wpabuf *probe; + int ret; + + wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); + + beacon = wpabuf_alloc(300); + if (beacon == NULL) + return -1; + probe = wpabuf_alloc(400); + if (probe == NULL) { + wpabuf_free(beacon); + return -1; + } + + if (wps_build_version(beacon) || + wps_build_wps_state(reg->wps, beacon) || + wps_build_ap_setup_locked(reg->wps, beacon) || + wps_build_selected_registrar(reg, beacon) || + wps_build_sel_reg_dev_password_id(reg, beacon) || + wps_build_sel_reg_config_methods(reg, beacon) || + wps_build_version(probe) || + wps_build_wps_state(reg->wps, probe) || + wps_build_ap_setup_locked(reg->wps, probe) || + wps_build_selected_registrar(reg, probe) || + wps_build_sel_reg_dev_password_id(reg, probe) || + wps_build_sel_reg_config_methods(reg, probe) || + wps_build_resp_type(reg, probe) || + wps_build_uuid_e(probe, reg->wps->uuid) || + wps_build_device_attrs(®->wps->dev, probe) || + wps_build_probe_config_methods(reg, probe) || + wps_build_rf_bands(®->wps->dev, probe)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + + beacon = wps_ie_encapsulate(beacon); + probe = wps_ie_encapsulate(probe); + + if (!beacon || !probe) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + + ret = wps_cb_set_ie(reg, beacon, probe); + wpabuf_free(beacon); + wpabuf_free(probe); + + return ret; +} + + +static int wps_get_dev_password(struct wps_data *wps) +{ + const u8 *pin; + size_t pin_len = 0; + + os_free(wps->dev_password); + wps->dev_password = NULL; + + if (wps->pbc) { + wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); + pin = (const u8 *) "00000000"; + pin_len = 8; + } else { + pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, + &pin_len); + } + if (pin == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password available for " + "the Enrollee"); + wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e, + &wps->peer_dev); + return -1; + } + + wps->dev_password = os_malloc(pin_len); + if (wps->dev_password == NULL) + return -1; + os_memcpy(wps->dev_password, pin, pin_len); + wps->dev_password_len = pin_len; + + return 0; +} + + +static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * UUID-R"); + wpabuf_put_be16(msg, ATTR_UUID_R); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN); + return 0; +} + + +static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg) +{ + u8 *hash; + const u8 *addr[4]; + size_t len[4]; + + if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: R-S2", + wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN); + + if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) { + wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for " + "R-Hash derivation"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: * R-Hash1"); + wpabuf_put_be16(msg, ATTR_R_HASH1); + wpabuf_put_be16(msg, SHA256_MAC_LEN); + hash = wpabuf_put(msg, SHA256_MAC_LEN); + /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */ + addr[0] = wps->snonce; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk1; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN); + + wpa_printf(MSG_DEBUG, "WPS: * R-Hash2"); + wpabuf_put_be16(msg, ATTR_R_HASH2); + wpabuf_put_be16(msg, SHA256_MAC_LEN); + hash = wpabuf_put(msg, SHA256_MAC_LEN); + /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */ + addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk2; + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN); + + return 0; +} + + +static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1"); + wpabuf_put_be16(msg, ATTR_R_SNONCE1); + wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); + wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN); + return 0; +} + + +static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2"); + wpabuf_put_be16(msg, ATTR_R_SNONCE2); + wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); + wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN, + WPS_SECRET_NONCE_LEN); + return 0; +} + + +static int wps_build_cred_network_idx(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * Network Index"); + wpabuf_put_be16(msg, ATTR_NETWORK_INDEX); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 1); + return 0; +} + + +static int wps_build_cred_ssid(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpabuf_put_be16(msg, ATTR_SSID); + wpabuf_put_be16(msg, cred->ssid_len); + wpabuf_put_data(msg, cred->ssid, cred->ssid_len); + return 0; +} + + +static int wps_build_cred_auth_type(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", + cred->auth_type); + wpabuf_put_be16(msg, ATTR_AUTH_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, cred->auth_type); + return 0; +} + + +static int wps_build_cred_encr_type(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", + cred->encr_type); + wpabuf_put_be16(msg, ATTR_ENCR_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, cred->encr_type); + return 0; +} + + +static int wps_build_cred_network_key(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * Network Key"); + wpabuf_put_be16(msg, ATTR_NETWORK_KEY); + wpabuf_put_be16(msg, cred->key_len); + wpabuf_put_data(msg, cred->key, cred->key_len); + return 0; +} + + +static int wps_build_cred_mac_addr(struct wpabuf *msg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", + MAC2STR(cred->mac_addr)); + wpabuf_put_be16(msg, ATTR_MAC_ADDR); + wpabuf_put_be16(msg, ETH_ALEN); + wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN); + return 0; +} + + +static int wps_build_credential(struct wpabuf *msg, + struct wps_credential *cred) +{ + if (wps_build_cred_network_idx(msg, cred) || + wps_build_cred_ssid(msg, cred) || + wps_build_cred_auth_type(msg, cred) || + wps_build_cred_encr_type(msg, cred) || + wps_build_cred_network_key(msg, cred) || + wps_build_cred_mac_addr(msg, cred)) + return -1; + return 0; +} + + +static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) +{ + struct wpabuf *cred; + + if (wps->wps->registrar->skip_cred_build) + goto skip_cred_build; + + wpa_printf(MSG_DEBUG, "WPS: * Credential"); + os_memset(&wps->cred, 0, sizeof(wps->cred)); + + os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); + wps->cred.ssid_len = wps->wps->ssid_len; + + /* Select the best authentication and encryption type */ + if (wps->auth_type & WPS_AUTH_WPA2PSK) + wps->auth_type = WPS_AUTH_WPA2PSK; + else if (wps->auth_type & WPS_AUTH_WPAPSK) + wps->auth_type = WPS_AUTH_WPAPSK; + else if (wps->auth_type & WPS_AUTH_OPEN) + wps->auth_type = WPS_AUTH_OPEN; + else if (wps->auth_type & WPS_AUTH_SHARED) + wps->auth_type = WPS_AUTH_SHARED; + else { + wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x", + wps->auth_type); + return -1; + } + wps->cred.auth_type = wps->auth_type; + + if (wps->auth_type == WPS_AUTH_WPA2PSK || + wps->auth_type == WPS_AUTH_WPAPSK) { + if (wps->encr_type & WPS_ENCR_AES) + wps->encr_type = WPS_ENCR_AES; + else if (wps->encr_type & WPS_ENCR_TKIP) + wps->encr_type = WPS_ENCR_TKIP; + else { + wpa_printf(MSG_DEBUG, "WPS: No suitable encryption " + "type for WPA/WPA2"); + return -1; + } + } else { + if (wps->encr_type & WPS_ENCR_WEP) + wps->encr_type = WPS_ENCR_WEP; + else if (wps->encr_type & WPS_ENCR_NONE) + wps->encr_type = WPS_ENCR_NONE; + else { + wpa_printf(MSG_DEBUG, "WPS: No suitable encryption " + "type for non-WPA/WPA2 mode"); + return -1; + } + } + wps->cred.encr_type = wps->encr_type; + /* Set MAC address in the Credential to be the AP's address (BSSID) */ + os_memcpy(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN); + + if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap && + !wps->wps->registrar->disable_auto_conf) { + u8 r[16]; + /* Generate a random passphrase */ + if (os_get_random(r, sizeof(r)) < 0) + return -1; + os_free(wps->new_psk); + wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len); + if (wps->new_psk == NULL) + return -1; + wps->new_psk_len--; /* remove newline */ + while (wps->new_psk_len && + wps->new_psk[wps->new_psk_len - 1] == '=') + wps->new_psk_len--; + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase", + wps->new_psk, wps->new_psk_len); + os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); + wps->cred.key_len = wps->new_psk_len; + } else if (wps->wps->network_key) { + os_memcpy(wps->cred.key, wps->wps->network_key, + wps->wps->network_key_len); + wps->cred.key_len = wps->wps->network_key_len; + } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { + char hex[65]; + /* Generate a random per-device PSK */ + os_free(wps->new_psk); + wps->new_psk_len = 32; + wps->new_psk = os_malloc(wps->new_psk_len); + if (wps->new_psk == NULL) + return -1; + if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) { + os_free(wps->new_psk); + wps->new_psk = NULL; + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK", + wps->new_psk, wps->new_psk_len); + wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk, + wps->new_psk_len); + os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2); + wps->cred.key_len = wps->new_psk_len * 2; + } + + cred = wpabuf_alloc(200); + if (cred == NULL) + return -1; + + if (wps_build_credential(cred, &wps->cred)) { + wpabuf_free(cred); + return -1; + } + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(cred)); + wpabuf_put_buf(msg, cred); + wpabuf_free(cred); + +skip_cred_build: + if (wps->wps->registrar->extra_cred) { + wpa_printf(MSG_DEBUG, "WPS: * Credential (pre-configured)"); + wpabuf_put_buf(msg, wps->wps->registrar->extra_cred); + } + + return 0; +} + + +static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS: * AP Settings"); + + if (wps_build_credential(msg, &wps->cred)) + return -1; + + return 0; +} + + +static struct wpabuf * wps_build_m2(struct wps_data *wps) +{ + struct wpabuf *msg; + + if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0) + return NULL; + wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", + wps->nonce_r, WPS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN); + + wpa_printf(MSG_DEBUG, "WPS: Building Message M2"); + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M2) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_uuid_r(wps, msg) || + wps_build_public_key(wps, msg) || + wps_derive_keys(wps) || + wps_build_auth_type_flags(wps, msg) || + wps_build_encr_type_flags(wps, msg) || + wps_build_conn_type_flags(wps, msg) || + wps_build_config_methods_r(wps->wps->registrar, msg) || + wps_build_device_attrs(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_assoc_state(wps, msg) || + wps_build_config_error(msg, WPS_CFG_NO_ERROR) || + wps_build_dev_password_id(msg, wps->dev_pw_id) || + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->state = RECV_M3; + return msg; +} + + +static struct wpabuf * wps_build_m2d(struct wps_data *wps) +{ + struct wpabuf *msg; + u16 err = WPS_CFG_NO_ERROR; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M2D"); + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps->wps->ap && wps->wps->ap_setup_locked) + err = WPS_CFG_SETUP_LOCKED; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M2D) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_uuid_r(wps, msg) || + wps_build_auth_type_flags(wps, msg) || + wps_build_encr_type_flags(wps, msg) || + wps_build_conn_type_flags(wps, msg) || + wps_build_config_methods_r(wps->wps->registrar, msg) || + wps_build_device_attrs(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_assoc_state(wps, msg) || + wps_build_config_error(msg, err) || + wps_build_os_version(&wps->wps->dev, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->state = RECV_M2D_ACK; + return msg; +} + + +static struct wpabuf * wps_build_m4(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M4"); + + wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + + plain = wpabuf_alloc(200); + if (plain == NULL) + return NULL; + + msg = wpabuf_alloc(1000); + if (msg == NULL) { + wpabuf_free(plain); + return NULL; + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M4) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_r_hash(wps, msg) || + wps_build_r_snonce1(wps, plain) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wps->state = RECV_M5; + return msg; +} + + +static struct wpabuf * wps_build_m6(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M6"); + + plain = wpabuf_alloc(200); + if (plain == NULL) + return NULL; + + msg = wpabuf_alloc(1000); + if (msg == NULL) { + wpabuf_free(plain); + return NULL; + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M6) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_r_snonce2(wps, plain) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wps->wps_pin_revealed = 1; + wps->state = RECV_M7; + return msg; +} + + +static struct wpabuf * wps_build_m8(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M8"); + + plain = wpabuf_alloc(500); + if (plain == NULL) + return NULL; + + msg = wpabuf_alloc(1000); + if (msg == NULL) { + wpabuf_free(plain); + return NULL; + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M8) || + wps_build_enrollee_nonce(wps, msg) || + (wps->wps->ap && wps_build_cred(wps, plain)) || + (!wps->wps->ap && wps_build_ap_settings(wps, plain)) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain) || + wps_build_authenticator(wps, msg)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wps->state = RECV_DONE; + return msg; +} + + +static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_ACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_NACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_config_error(msg, wps->config_error)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, + enum wsc_op_code *op_code) +{ + struct wpabuf *msg; + +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp) { + struct upnp_pending_message *p, *prev = NULL; + if (wps->ext_reg > 1) + wps_registrar_free_pending_m2(wps->wps); + p = wps->wps->upnp_msgs; + /* TODO: check pending message MAC address */ + while (p && p->next) { + prev = p; + p = p->next; + } + if (p) { + wpa_printf(MSG_DEBUG, "WPS: Use pending message from " + "UPnP"); + if (prev) + prev->next = NULL; + else + wps->wps->upnp_msgs = NULL; + msg = p->msg; + os_free(p); + *op_code = WSC_MSG; + if (wps->ext_reg == 0) + wps->ext_reg = 1; + return msg; + } + } + if (wps->ext_reg) { + wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no " + "pending message available"); + return NULL; + } +#endif /* CONFIG_WPS_UPNP */ + + switch (wps->state) { + case SEND_M2: + if (wps_get_dev_password(wps) < 0) + msg = wps_build_m2d(wps); + else + msg = wps_build_m2(wps); + *op_code = WSC_MSG; + break; + case SEND_M2D: + msg = wps_build_m2d(wps); + *op_code = WSC_MSG; + break; + case SEND_M4: + msg = wps_build_m4(wps); + *op_code = WSC_MSG; + break; + case SEND_M6: + msg = wps_build_m6(wps); + *op_code = WSC_MSG; + break; + case SEND_M8: + msg = wps_build_m8(wps); + *op_code = WSC_MSG; + break; + case RECV_DONE: + msg = wps_build_wsc_ack(wps); + *op_code = WSC_ACK; + break; + case SEND_WSC_NACK: + msg = wps_build_wsc_nack(wps); + *op_code = WSC_NACK; + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building " + "a message", wps->state); + msg = NULL; + break; + } + + if (*op_code == WSC_MSG && msg) { + /* Save a copy of the last message for Authenticator derivation + */ + wpabuf_free(wps->last_msg); + wps->last_msg = wpabuf_dup(msg); + } + + return msg; +} + + +static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce) +{ + if (e_nonce == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received"); + return -1; + } + + os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", + wps->nonce_e, WPS_NONCE_LEN); + + return 0; +} + + +static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce) +{ + if (r_nonce == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received"); + return -1; + } + + if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received"); + return -1; + } + + return 0; +} + + +static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e) +{ + if (uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E received"); + return -1; + } + + os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN); + + return 0; +} + + +static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id) +{ + if (pw_id == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received"); + return -1; + } + + wps->dev_pw_id = WPA_GET_BE16(pw_id); + wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id); + + return 0; +} + + +static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1) +{ + if (e_hash1 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received"); + return -1; + } + + os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN); + + return 0; +} + + +static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2) +{ + if (e_hash2 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received"); + return -1; + } + + os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN); + + return 0; +} + + +static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + + if (e_snonce1 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1, + WPS_SECRET_NONCE_LEN); + + /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */ + addr[0] = e_snonce1; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk1; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + + if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does " + "not match with the pre-committed value"); + wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; + wps_pwd_auth_fail_event(wps->wps, 0, 1); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first " + "half of the device password"); + + return 0; +} + + +static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2) +{ + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + + if (e_snonce2 == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2, + WPS_SECRET_NONCE_LEN); + + /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */ + addr[0] = e_snonce2; + len[0] = WPS_SECRET_NONCE_LEN; + addr[1] = wps->psk2; + len[1] = WPS_PSK_LEN; + addr[2] = wpabuf_head(wps->dh_pubkey_e); + len[2] = wpabuf_len(wps->dh_pubkey_e); + addr[3] = wpabuf_head(wps->dh_pubkey_r); + len[3] = wpabuf_len(wps->dh_pubkey_r); + hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); + + if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does " + "not match with the pre-committed value"); + wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); + wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; + wps_pwd_auth_fail_event(wps->wps, 0, 2); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second " + "half of the device password"); + wps->wps_pin_revealed = 0; + wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e); + + return 0; +} + + +static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr) +{ + if (mac_addr == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No MAC Address received"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR, + MAC2STR(mac_addr)); + os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN); + os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN); + + return 0; +} + + +static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, + size_t pk_len) +{ + if (pk == NULL || pk_len == 0) { + wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); + return -1; + } + + wpabuf_free(wps->dh_pubkey_e); + wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); + if (wps->dh_pubkey_e == NULL) + return -1; + + return 0; +} + + +static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) +{ + u16 auth_types; + + if (auth == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags " + "received"); + return -1; + } + + auth_types = WPA_GET_BE16(auth); + + wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x", + auth_types); + wps->auth_type = wps->wps->auth_types & auth_types; + if (wps->auth_type == 0) { + wpa_printf(MSG_DEBUG, "WPS: No match in supported " + "authentication types (own 0x%x Enrollee 0x%x)", + wps->wps->auth_types, auth_types); + return -1; + } + + return 0; +} + + +static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr) +{ + u16 encr_types; + + if (encr == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags " + "received"); + return -1; + } + + encr_types = WPA_GET_BE16(encr); + + wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x", + encr_types); + wps->encr_type = wps->wps->encr_types & encr_types; + if (wps->encr_type == 0) { + wpa_printf(MSG_DEBUG, "WPS: No match in supported " + "encryption types"); + return -1; + } + + return 0; +} + + +static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn) +{ + if (conn == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags " + "received"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x", + *conn); + + return 0; +} + + +static int wps_process_config_methods(struct wps_data *wps, const u8 *methods) +{ + u16 m; + + if (methods == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Config Methods received"); + return -1; + } + + m = WPA_GET_BE16(methods); + + wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x", m); + + return 0; +} + + +static int wps_process_wps_state(struct wps_data *wps, const u8 *state) +{ + if (state == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State " + "received"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d", + *state); + + return 0; +} + + +static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc) +{ + u16 a; + + if (assoc == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Association State received"); + return -1; + } + + a = WPA_GET_BE16(assoc); + wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a); + + return 0; +} + + +static int wps_process_config_error(struct wps_data *wps, const u8 *err) +{ + u16 e; + + if (err == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received"); + return -1; + } + + e = WPA_GET_BE16(err); + wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e); + + return 0; +} + + +static enum wps_process_res wps_process_m1(struct wps_data *wps, + struct wps_parse_attr *attr) +{ + wpa_printf(MSG_DEBUG, "WPS: Received M1"); + + if (wps->state != RECV_M1) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M1", wps->state); + return WPS_FAILURE; + } + + if (wps_process_uuid_e(wps, attr->uuid_e) || + wps_process_mac_addr(wps, attr->mac_addr) || + wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || + wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || + wps_process_auth_type_flags(wps, attr->auth_type_flags) || + wps_process_encr_type_flags(wps, attr->encr_type_flags) || + wps_process_conn_type_flags(wps, attr->conn_type_flags) || + wps_process_config_methods(wps, attr->config_methods) || + wps_process_wps_state(wps, attr->wps_state) || + wps_process_device_attrs(&wps->peer_dev, attr) || + wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) || + wps_process_assoc_state(wps, attr->assoc_state) || + wps_process_dev_password_id(wps, attr->dev_password_id) || + wps_process_config_error(wps, attr->config_error) || + wps_process_os_version(&wps->peer_dev, attr->os_version)) + return WPS_FAILURE; + + if (wps->dev_pw_id != DEV_PW_DEFAULT && + wps->dev_pw_id != DEV_PW_USER_SPECIFIED && + wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && + wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED && + (wps->dev_pw_id != DEV_PW_PUSHBUTTON || + !wps->wps->registrar->pbc)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d", + wps->dev_pw_id); + wps->state = SEND_M2D; + return WPS_CONTINUE; + } + + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { + if (wps_registrar_pbc_overlap(wps->wps->registrar, + wps->mac_addr_e, wps->uuid_e)) { + wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " + "negotiation"); + wps->state = SEND_M2D; + return WPS_CONTINUE; + } + wps_registrar_add_pbc_session(wps->wps->registrar, + wps->mac_addr_e, wps->uuid_e); + wps->pbc = 1; + } + + wps->state = SEND_M2; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m3(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + wpa_printf(MSG_DEBUG, "WPS: Received M3"); + + if (wps->state != RECV_M3) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M3", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg) || + wps_process_e_hash1(wps, attr->e_hash1) || + wps_process_e_hash2(wps, attr->e_hash2)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wps->state = SEND_M4; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_m5(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + wpa_printf(MSG_DEBUG, "WPS: Received M5"); + + if (wps->state != RECV_M5) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M5", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + "Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " + "attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || + wps_process_e_snonce1(wps, eattr.e_snonce1)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = SEND_M6; + return WPS_CONTINUE; +} + + +static int wps_process_ap_settings_r(struct wps_data *wps, + struct wps_parse_attr *attr) +{ + if (wps->wps->ap) + return 0; + + /* AP Settings Attributes in M7 when Enrollee is an AP */ + if (wps_process_ap_settings(attr, &wps->cred) < 0) + return -1; + + wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP"); + + /* + * TODO: Provide access to AP settings and allow changes before sending + * out M8. For now, just copy the settings unchanged into M8. + */ + + return 0; +} + + +static enum wps_process_res wps_process_m7(struct wps_data *wps, + const struct wpabuf *msg, + struct wps_parse_attr *attr) +{ + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + wpa_printf(MSG_DEBUG, "WPS: Received M7"); + + if (wps->state != RECV_M7) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving M7", wps->state); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || + wps_process_authenticator(wps, attr->authenticator, msg)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + "Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " + "attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || + wps_process_e_snonce2(wps, eattr.e_snonce2) || + wps_process_ap_settings_r(wps, &eattr)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpabuf_free(decrypted); + + wps->state = SEND_M8; + return WPS_CONTINUE; +} + + +static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + enum wps_process_res ret = WPS_CONTINUE; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG"); + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_M1 && + (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, + WPS_NONCE_LEN != 0))) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + return WPS_FAILURE; + } + + switch (*attr.msg_type) { + case WPS_M1: +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && attr.mac_addr) { + /* Remove old pending messages when starting new run */ + wps_free_pending_msgs(wps->wps->upnp_msgs); + wps->wps->upnp_msgs = NULL; + + upnp_wps_device_send_wlan_event( + wps->wps->wps_upnp, attr.mac_addr, + UPNP_WPS_WLANEVENT_TYPE_EAP, msg); + } +#endif /* CONFIG_WPS_UPNP */ + ret = wps_process_m1(wps, &attr); + break; + case WPS_M3: + ret = wps_process_m3(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M3); + break; + case WPS_M5: + ret = wps_process_m5(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M5); + break; + case WPS_M7: + ret = wps_process_m7(wps, msg, &attr); + if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) + wps_fail_event(wps->wps, WPS_M7); + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + + if (ret == WPS_CONTINUE) { + /* Save a copy of the last message for Authenticator derivation + */ + wpabuf_free(wps->last_msg); + wps->last_msg = wpabuf_dup(msg); + } + + return ret; +} + + +static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK"); + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_WSC_ACK) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK && + upnp_wps_subscribers(wps->wps->wps_upnp)) { + if (wps->wps->upnp_msgs) + return WPS_CONTINUE; + wpa_printf(MSG_DEBUG, "WPS: Wait for response from an " + "external Registrar"); + return WPS_PENDING; + } +#endif /* CONFIG_WPS_UPNP */ + + if (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + return WPS_FAILURE; + } + + if (wps->state == RECV_M2D_ACK) { +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && + upnp_wps_subscribers(wps->wps->wps_upnp)) { + if (wps->wps->upnp_msgs) + return WPS_CONTINUE; + if (wps->ext_reg == 0) + wps->ext_reg = 1; + wpa_printf(MSG_DEBUG, "WPS: Wait for response from an " + "external Registrar"); + return WPS_PENDING; + } +#endif /* CONFIG_WPS_UPNP */ + + wpa_printf(MSG_DEBUG, "WPS: No more registrars available - " + "terminate negotiation"); + } + + return WPS_FAILURE; +} + + +static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + int old_state; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); + + old_state = wps->state; + wps->state = SEND_WSC_NACK; + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_WSC_NACK) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && wps->ext_reg) { + wpa_printf(MSG_DEBUG, "WPS: Negotiation using external " + "Registrar terminated by the Enrollee"); + return WPS_FAILURE; + } +#endif /* CONFIG_WPS_UPNP */ + + if (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + return WPS_FAILURE; + } + + if (attr.config_error == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute " + "in WSC_NACK"); + return WPS_FAILURE; + } + + wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with " + "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + + switch (old_state) { + case RECV_M3: + wps_fail_event(wps->wps, WPS_M2); + break; + case RECV_M5: + wps_fail_event(wps->wps, WPS_M4); + break; + case RECV_M7: + wps_fail_event(wps->wps, WPS_M6); + break; + case RECV_DONE: + wps_fail_event(wps->wps, WPS_M8); + break; + default: + break; + } + + return WPS_FAILURE; +} + + +static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done"); + + if (wps->state != RECV_DONE && + (!wps->wps->wps_upnp || !wps->ext_reg)) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " + "receiving WSC_Done", wps->state); + return WPS_FAILURE; + } + + if (wps_parse_msg(msg, &attr) < 0) + return WPS_FAILURE; + + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", + attr.version ? *attr.version : 0); + return WPS_FAILURE; + } + + if (attr.msg_type == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); + return WPS_FAILURE; + } + + if (*attr.msg_type != WPS_WSC_DONE) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", + *attr.msg_type); + return WPS_FAILURE; + } + +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && wps->ext_reg) { + wpa_printf(MSG_DEBUG, "WPS: Negotiation using external " + "Registrar completed successfully"); + return WPS_DONE; + } +#endif /* CONFIG_WPS_UPNP */ + + if (attr.registrar_nonce == NULL || + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); + return WPS_FAILURE; + } + + if (attr.enrollee_nonce == NULL || + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); + return WPS_FAILURE; + } + + wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully"); + + if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk && + wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { + struct wps_credential cred; + + wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based " + "on first Enrollee connection"); + + os_memset(&cred, 0, sizeof(cred)); + os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len); + cred.ssid_len = wps->wps->ssid_len; + cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + os_memcpy(cred.key, wps->new_psk, wps->new_psk_len); + cred.key_len = wps->new_psk_len; + + wps->wps->wps_state = WPS_STATE_CONFIGURED; + wpa_hexdump_ascii_key(MSG_DEBUG, + "WPS: Generated random passphrase", + wps->new_psk, wps->new_psk_len); + if (wps->wps->cred_cb) + wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + + os_free(wps->new_psk); + wps->new_psk = NULL; + } + + if (!wps->wps->ap) { + wpa_printf(MSG_DEBUG, "WPS: Update local configuration based " + "on the modified AP configuration"); + if (wps->wps->cred_cb) + wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + } + + if (wps->new_psk) { + if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, + wps->new_psk, wps->new_psk_len)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to configure the " + "new PSK"); + } + os_free(wps->new_psk); + wps->new_psk = NULL; + } + + wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); + + if (wps->pbc) { + wps_registrar_remove_pbc_session(wps->wps->registrar, + wps->mac_addr_e, wps->uuid_e); + wps_registrar_pbc_completed(wps->wps->registrar); + } + + wps_success_event(wps->wps); + + return WPS_DONE; +} + + +enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, + enum wsc_op_code op_code, + const struct wpabuf *msg) +{ + enum wps_process_res ret; + + wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu " + "op_code=%d)", + (unsigned long) wpabuf_len(msg), op_code); + +#ifdef CONFIG_WPS_UPNP + if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) { + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type && + *attr.msg_type == WPS_M3) + wps->ext_reg = 2; /* past M2/M2D phase */ + } + if (wps->ext_reg > 1) + wps_registrar_free_pending_m2(wps->wps); + if (wps->wps->wps_upnp && wps->ext_reg && + wps->wps->upnp_msgs == NULL && + (op_code == WSC_MSG || op_code == WSC_Done)) { + struct wps_parse_attr attr; + int type; + if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL) + type = -1; + else + type = *attr.msg_type; + wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)" + " to external Registrar for processing", type); + upnp_wps_device_send_wlan_event(wps->wps->wps_upnp, + wps->mac_addr_e, + UPNP_WPS_WLANEVENT_TYPE_EAP, + msg); + if (op_code == WSC_MSG) + return WPS_PENDING; + } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) { + wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using " + "external Registrar"); + return WPS_CONTINUE; + } +#endif /* CONFIG_WPS_UPNP */ + + switch (op_code) { + case WSC_MSG: + return wps_process_wsc_msg(wps, msg); + case WSC_ACK: + return wps_process_wsc_ack(wps, msg); + case WSC_NACK: + return wps_process_wsc_nack(wps, msg); + case WSC_Done: + ret = wps_process_wsc_done(wps, msg); + if (ret == WPS_FAILURE) { + wps->state = SEND_WSC_NACK; + wps_fail_event(wps->wps, WPS_WSC_DONE); + } + return ret; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); + return WPS_FAILURE; + } +} + + +int wps_registrar_update_ie(struct wps_registrar *reg) +{ + return wps_set_ie(reg); +} + + +static void wps_registrar_set_selected_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wps_registrar *reg = eloop_ctx; + + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar timed out - " + "unselect Registrar"); + reg->selected_registrar = 0; + reg->pbc = 0; + reg->sel_reg_dev_password_id_override = -1; + reg->sel_reg_config_methods_override = -1; + wps_set_ie(reg); +} + + +/** + * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar + * @reg: Registrar data from wps_registrar_init() + * @msg: Received message from SetSelectedRegistrar + * @msg_len: Length of msg in octets + * Returns: 0 on success, -1 on failure + * + * This function is called when an AP receives a SetSelectedRegistrar UPnP + * message. + */ +int wps_registrar_set_selected_registrar(struct wps_registrar *reg, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", + msg); + + if (wps_parse_msg(msg, &attr) < 0) + return -1; + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " + "version 0x%x", attr.version ? *attr.version : 0); + return -1; + } + + if (attr.selected_registrar == NULL || + *attr.selected_registrar == 0) { + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " + "Selected Registrar"); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, + NULL); + wps_registrar_set_selected_timeout(reg, NULL); + return 0; + } + + reg->selected_registrar = 1; + reg->sel_reg_dev_password_id_override = attr.dev_password_id ? + WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; + reg->sel_reg_config_methods_override = attr.sel_reg_config_methods ? + WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + wps_set_ie(reg); + + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); + return 0; +} diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c new file mode 100644 index 0000000000000..6d2de0e084594 --- /dev/null +++ b/src/wps/wps_upnp.c @@ -0,0 +1,1056 @@ +/* + * UPnP WPS Device + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See below for more details on licensing and code history. + */ + +/* + * This has been greatly stripped down from the original file + * (upnp_wps_device.c) by Ted Merrill, Atheros Communications + * in order to eliminate use of the bulky libupnp library etc. + * + * History: + * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and + * the libupnp library. + * The layering (by Sony) was well done; only a very minor modification + * to API of upnp_wps_device.c was required. + * libupnp was found to be undesirable because: + * -- It consumed too much code and data space + * -- It uses multiple threads, making debugging more difficult + * and possibly reducing reliability. + * -- It uses static variables and only supports one instance. + * The shim and libupnp are here replaced by special code written + * specifically for the needs of hostapd. + * Various shortcuts can and are taken to keep the code size small. + * Generally, execution time is not as crucial. + * + * BUGS: + * -- UPnP requires that we be able to resolve domain names. + * While uncommon, if we have to do it then it will stall the entire + * hostapd program, which is bad. + * This is because we use the standard linux getaddrinfo() function + * which is syncronous. + * An asyncronous solution would be to use the free "ares" library. + * -- Does not have a robust output buffering scheme. Uses a single + * fixed size output buffer per TCP/HTTP connection, with possible (although + * unlikely) possibility of overflow and likely excessive use of RAM. + * A better solution would be to write the HTTP output as a buffered stream, + * using chunking: (handle header specially, then) generate data with + * a printf-like function into a buffer, catching buffer full condition, + * then send it out surrounded by http chunking. + * -- There is some code that could be separated out into the common + * library to be shared with wpa_supplicant. + * -- Needs renaming with module prefix to avoid polluting the debugger + * namespace and causing possible collisions with other static fncs + * and structure declarations when using the debugger. + * -- Just what should be in the first event message sent after subscription + * for the WLANEvent field? If i pass it empty, Vista replies with OK + * but apparently barfs on the message. + * -- The http error code generation is pretty bogus, hopefully noone cares. + * + * Author: Ted Merrill, Atheros Communications, based upon earlier work + * as explained above and below. + * + * Copyright: + * Copyright 2008 Atheros Communications. + * + * The original header (of upnp_wps_device.c) reads: + * + * Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved. + * + * File Name: upnp_wps_device.c + * Description: EAP-WPS UPnP device source + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Sony Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c + * typical header: + * + * Copyright (c) 2000-2003 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Overview of WPS over UPnP: + * + * UPnP is a protocol that allows devices to discover each other and control + * each other. In UPnP terminology, a device is either a "device" (a server + * that provides information about itself and allows itself to be controlled) + * or a "control point" (a client that controls "devices") or possibly both. + * This file implements a UPnP "device". + * + * For us, we use mostly basic UPnP discovery, but the control part of interest + * is WPS carried via UPnP messages. There is quite a bit of basic UPnP + * discovery to do before we can get to WPS, however. + * + * UPnP discovery begins with "devices" send out multicast UDP packets to a + * certain fixed multicast IP address and port, and "control points" sending + * out other such UDP packets. + * + * The packets sent by devices are NOTIFY packets (not to be confused with TCP + * NOTIFY packets that are used later) and those sent by control points are + * M-SEARCH packets. These packets contain a simple HTTP style header. The + * packets are sent redundantly to get around packet loss. Devices respond to + * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK + * messages, which give similar information as the UDP NOTIFY packets. + * + * The above UDP packets advertise the (arbitrary) TCP ports that the + * respective parties will listen to. The control point can then do a HTTP + * SUBSCRIBE (something like an HTTP PUT) after which the device can do a + * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging. + * + * The control point will also do HTTP GET of the "device file" listed in the + * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE + * data), and based on this will do additional GETs... HTTP POSTs are done to + * cause an action. + * + * Beyond some basic information in HTTP headers, additional information is in + * the HTTP bodies, in a format set by the SOAP and XML standards, a markup + * language related to HTML used for web pages. This language is intended to + * provide the ultimate in self-documentation by providing a universal + * namespace based on pseudo-URLs called URIs. Note that although a URI looks + * like a URL (a web address), they are never accessed as such but are used + * only as identifiers. + * + * The POST of a GetDeviceInfo gets information similar to what might be + * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8 + * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted + * to a bin64 ascii representation for encapsulation. When proxying messages, + * WLANEvent and PutWLANResponse are used. + * + * This of course glosses over a lot of details. + */ + +#include "includes.h" + +#include <assert.h> +#include <net/if.h> +#include <netdb.h> +#include <sys/ioctl.h> + +#include "common.h" +#include "uuid.h" +#include "base64.h" +#include "wps.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + + +/* + * UPnP allows a client ("control point") to send a server like us ("device") + * a domain name for registration, and we are supposed to resolve it. This is + * bad because, using the standard Linux library, we will stall the entire + * hostapd waiting for resolution. + * + * The "correct" solution would be to use an event driven library for domain + * name resolution such as "ares". However, this would increase code size + * further. Since it is unlikely that we'll actually see such domain names, we + * can just refuse to accept them. + */ +#define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */ + + +/* + * UPnP does not scale well. If we were in a room with thousands of control + * points then potentially we could be expected to handle subscriptions for + * each of them, which would exhaust our memory. So we must set a limit. In + * practice we are unlikely to see more than one or two. + */ +#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */ +#define MAX_ADDR_PER_SUBSCRIPTION 8 + + +/* Write the current date/time per RFC */ +void format_date(struct wpabuf *buf) +{ + const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; + const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0" + "Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; + struct tm *date; + time_t t; + + t = time(NULL); + date = gmtime(&t); + wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT", + &weekday_str[date->tm_wday * 4], date->tm_mday, + &month_str[date->tm_mon * 4], date->tm_year + 1900, + date->tm_hour, date->tm_min, date->tm_sec); +} + + +/*************************************************************************** + * UUIDs (unique identifiers) + * + * These are supposed to be unique in all the world. + * Sometimes permanent ones are used, sometimes temporary ones + * based on random numbers... there are different rules for valid content + * of different types. + * Each uuid is 16 bytes long. + **************************************************************************/ + +/* uuid_make -- construct a random UUID + * The UPnP documents don't seem to offer any guidelines as to which method to + * use for constructing UUIDs for subscriptions. Presumably any method from + * rfc4122 is good enough; I've chosen random number method. + */ +static void uuid_make(u8 uuid[UUID_LEN]) +{ + os_get_random(uuid, UUID_LEN); + + /* Replace certain bits as specified in rfc4122 or X.667 */ + uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */ + uuid[8] &= 0x3f; uuid[8] |= 0x80; +} + + +/* + * Subscriber address handling. + * Since a subscriber may have an arbitrary number of addresses, we have to + * add a bunch of code to handle them. + * + * Addresses are passed in text, and MAY be domain names instead of the (usual + * and expected) dotted IP addresses. Resolving domain names consumes a lot of + * resources. Worse, we are currently using the standard Linux getaddrinfo() + * which will block the entire program until complete or timeout! The proper + * solution would be to use the "ares" library or similar with more state + * machine steps etc. or just disable domain name resolution by setting + * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file. + */ + +/* subscr_addr_delete -- delete single unlinked subscriber address + * (be sure to unlink first if need be) + */ +static void subscr_addr_delete(struct subscr_addr *a) +{ + /* + * Note: do NOT free domain_and_port or path because they point to + * memory within the allocation of "a". + */ + os_free(a); +} + + +/* subscr_addr_unlink -- unlink subscriber address from linked list */ +static void subscr_addr_unlink(struct subscription *s, struct subscr_addr *a) +{ + struct subscr_addr **listp = &s->addr_list; + s->n_addr--; + a->next->prev = a->prev; + a->prev->next = a->next; + if (*listp == a) { + if (a == a->next) { + /* last in queue */ + *listp = NULL; + assert(s->n_addr == 0); + } else { + *listp = a->next; + } + } +} + + +/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */ +static void subscr_addr_free_all(struct subscription *s) +{ + struct subscr_addr **listp = &s->addr_list; + struct subscr_addr *a; + while ((a = *listp) != NULL) { + subscr_addr_unlink(s, a); + subscr_addr_delete(a); + } +} + + +/* subscr_addr_link -- add subscriber address to list of addresses */ +static void subscr_addr_link(struct subscription *s, struct subscr_addr *a) +{ + struct subscr_addr **listp = &s->addr_list; + s->n_addr++; + if (*listp == NULL) { + *listp = a->next = a->prev = a; + } else { + a->next = *listp; + a->prev = (*listp)->prev; + a->prev->next = a; + a->next->prev = a; + } +} + + +/* subscr_addr_add_url -- add address(es) for one url to subscription */ +static void subscr_addr_add_url(struct subscription *s, const char *url) +{ + int alloc_len; + char *scratch_mem = NULL; + char *mem; + char *domain_and_port; + char *delim; + char *path; + char *domain; + int port = 80; /* port to send to (default is port 80) */ + struct addrinfo hints; + struct addrinfo *result = NULL; + struct addrinfo *rp; + int rerr; + struct subscr_addr *a = NULL; + + /* url MUST begin with http: */ + if (os_strncasecmp(url, "http://", 7)) + goto fail; + url += 7; + + /* allocate memory for the extra stuff we need */ + alloc_len = (2 * (os_strlen(url) + 1)); + scratch_mem = os_zalloc(alloc_len); + if (scratch_mem == NULL) + goto fail; + mem = scratch_mem; + strcpy(mem, url); + domain_and_port = mem; + mem += 1 + os_strlen(mem); + delim = os_strchr(domain_and_port, '/'); + if (delim) { + *delim++ = 0; /* null terminate domain and port */ + path = delim; + } else { + path = domain_and_port + os_strlen(domain_and_port); + } + domain = mem; + strcpy(domain, domain_and_port); + delim = strchr(domain, ':'); + if (delim) { + *delim++ = 0; /* null terminate domain */ + if (isdigit(*delim)) + port = atol(delim); + } + + /* + * getaddrinfo does the right thing with dotted decimal notations, or + * will resolve domain names. Resolving domain names will unfortunately + * hang the entire program until it is resolved or it times out + * internal to getaddrinfo; fortunately we think that the use of actual + * domain names (vs. dotted decimal notations) should be uncommon. + */ + os_memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; /* IPv4 */ + hints.ai_socktype = SOCK_STREAM; +#if NO_DOMAIN_NAME_RESOLUTION + /* Suppress domain name resolutions that would halt + * the program for periods of time + */ + hints.ai_flags = AI_NUMERICHOST; +#else + /* Allow domain name resolution. */ + hints.ai_flags = 0; +#endif + hints.ai_protocol = 0; /* Any protocol? */ + rerr = getaddrinfo(domain, NULL /* fill in port ourselves */, + &hints, &result); + if (rerr) { + wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", + rerr, gai_strerror(rerr), domain); + goto fail; + } + for (rp = result; rp; rp = rp->ai_next) { + /* Limit no. of address to avoid denial of service attack */ + if (s->n_addr >= MAX_ADDR_PER_SUBSCRIPTION) { + wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " + "Ignoring excessive addresses"); + break; + } + + a = os_zalloc(sizeof(*a) + alloc_len); + if (a == NULL) + continue; + a->s = s; + mem = (void *) (a + 1); + a->domain_and_port = mem; + strcpy(mem, domain_and_port); + mem += 1 + strlen(mem); + a->path = mem; + if (path[0] != '/') + *mem++ = '/'; + strcpy(mem, path); + mem += 1 + strlen(mem); + os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); + a->saddr.sin_port = htons(port); + + subscr_addr_link(s, a); + a = NULL; /* don't free it below */ + } + +fail: + if (result) + freeaddrinfo(result); + os_free(scratch_mem); + os_free(a); +} + + +/* subscr_addr_list_create -- create list from urls in string. + * Each url is enclosed by angle brackets. + */ +static void subscr_addr_list_create(struct subscription *s, + const char *url_list) +{ + char *end; + for (;;) { + while (*url_list == ' ' || *url_list == '\t') + url_list++; + if (*url_list != '<') + break; + url_list++; + end = os_strchr(url_list, '>'); + if (end == NULL) + break; + *end++ = 0; + subscr_addr_add_url(s, url_list); + url_list = end; + } +} + + +int send_wpabuf(int fd, struct wpabuf *buf) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", + (unsigned long) wpabuf_len(buf)); + errno = 0; + if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != + (int) wpabuf_len(buf)) { + wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: " + "errno=%d (%s)", + errno, strerror(errno)); + return -1; + } + + return 0; +} + + +static void wpabuf_put_property(struct wpabuf *buf, const char *name, + const char *value) +{ + wpabuf_put_str(buf, "<e:property>"); + wpabuf_printf(buf, "<%s>", name); + if (value) + wpabuf_put_str(buf, value); + wpabuf_printf(buf, "</%s>", name); + wpabuf_put_str(buf, "</e:property>\n"); +} + + +/** + * upnp_wps_device_send_event - Queue event messages for subscribers + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * + * This function queues the last WLANEvent to be sent for all currently + * subscribed UPnP control points. sm->wlanevent must have been set with the + * encoded data before calling this function. + */ +static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) +{ + /* Enqueue event message for all subscribers */ + struct wpabuf *buf; /* holds event message */ + int buf_size = 0; + struct subscription *s; + /* Actually, utf-8 is the default, but it doesn't hurt to specify it */ + const char *format_head = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; + const char *format_tail = "</e:propertyset>\n"; + + if (sm->subscriptions == NULL) { + /* optimize */ + return; + } + + /* Determine buffer size needed first */ + buf_size += os_strlen(format_head); + buf_size += 50 + 2 * os_strlen("WLANEvent"); + if (sm->wlanevent) + buf_size += os_strlen(sm->wlanevent); + buf_size += os_strlen(format_tail); + + buf = wpabuf_alloc(buf_size); + if (buf == NULL) + return; + wpabuf_put_str(buf, format_head); + wpabuf_put_property(buf, "WLANEvent", sm->wlanevent); + wpabuf_put_str(buf, format_tail); + + wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s", + (char *) wpabuf_head(buf)); + + s = sm->subscriptions; + do { + if (event_add(s, buf)) { + struct subscription *s_old = s; + wpa_printf(MSG_INFO, "WPS UPnP: Dropping " + "subscriber due to event backlog"); + s = s_old->next; + subscription_unlink(s_old); + subscription_destroy(s_old); + } else { + s = s->next; + } + } while (s != sm->subscriptions); + + wpabuf_free(buf); +} + + +/* + * Event subscription (subscriber machines register with us to receive event + * messages). + * This is the result of an incoming HTTP over TCP SUBSCRIBE request. + */ + +/* subscription_unlink -- remove from the active list */ +void subscription_unlink(struct subscription *s) +{ + struct upnp_wps_device_sm *sm = s->sm; + + if (s->next == s) { + /* only one? */ + sm->subscriptions = NULL; + } else { + if (sm->subscriptions == s) + sm->subscriptions = s->next; + s->next->prev = s->prev; + s->prev->next = s->next; + } + sm->n_subscriptions--; +} + + +/* subscription_link_to_end -- link to end of active list + * (should have high expiry time!) + */ +static void subscription_link_to_end(struct subscription *s) +{ + struct upnp_wps_device_sm *sm = s->sm; + + if (sm->subscriptions) { + s->next = sm->subscriptions; + s->prev = s->next->prev; + s->prev->next = s; + s->next->prev = s; + } else { + sm->subscriptions = s->next = s->prev = s; + } + sm->n_subscriptions++; +} + + +/* subscription_destroy -- destroy an unlinked subscription + * Be sure to unlink first if necessary. + */ +void subscription_destroy(struct subscription *s) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); + if (s->addr_list) + subscr_addr_free_all(s); + event_delete_all(s); + os_free(s); +} + + +/* subscription_list_age -- remove expired subscriptions */ +static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) +{ + struct subscription *s; + while ((s = sm->subscriptions) != NULL && s->timeout_time < now) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription"); + subscription_unlink(s); + subscription_destroy(s); + } +} + + +/* subscription_find -- return existing subscription matching uuid, if any + * returns NULL if not found + */ +struct subscription * subscription_find(struct upnp_wps_device_sm *sm, + const u8 uuid[UUID_LEN]) +{ + struct subscription *s0 = sm->subscriptions; + struct subscription *s = s0; + + if (s0 == NULL) + return NULL; + do { + if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0) + return s; /* Found match */ + s = s->next; + } while (s != s0); + + return NULL; +} + + +/* subscription_first_event -- send format/queue event that is automatically + * sent on a new subscription. + */ +static int subscription_first_event(struct subscription *s) +{ + /* + * Actually, utf-8 is the default, but it doesn't hurt to specify it. + * + * APStatus is apparently a bit set, + * 0x1 = configuration change (but is always set?) + * 0x10 = ap is locked + * + * Per UPnP spec, we send out the last value of each variable, even + * for WLANEvent, whatever it was. + */ + char *wlan_event; + struct wpabuf *buf; + int ap_status = 1; /* TODO: add 0x10 if access point is locked */ + const char *head = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; + const char *tail = "</e:propertyset>\n"; + char txt[10]; + + wlan_event = s->sm->wlanevent; + if (wlan_event == NULL || *wlan_event == '\0') { + wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for " + "initial event message"); + wlan_event = ""; + } + buf = wpabuf_alloc(500 + os_strlen(wlan_event)); + if (buf == NULL) + return 1; + + wpabuf_put_str(buf, head); + wpabuf_put_property(buf, "STAStatus", "1"); + os_snprintf(txt, sizeof(txt), "%d", ap_status); + wpabuf_put_property(buf, "APStatus", txt); + if (*wlan_event) + wpabuf_put_property(buf, "WLANEvent", wlan_event); + wpabuf_put_str(buf, tail); + + if (event_add(s, buf)) { + wpabuf_free(buf); + return 1; + } + wpabuf_free(buf); + + return 0; +} + + +/** + * subscription_start - Rremember a UPnP control point to send events to. + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @callback_urls: malloc' mem given to the subscription + * Returns: %NULL on error, or pointer to new subscription structure. + */ +struct subscription * subscription_start(struct upnp_wps_device_sm *sm, + char *callback_urls) +{ + struct subscription *s; + time_t now = time(NULL); + time_t expire = now + UPNP_SUBSCRIBE_SEC; + + /* Get rid of expired subscriptions so we have room */ + subscription_list_age(sm, now); + + /* If too many subscriptions, remove oldest */ + if (sm->n_subscriptions >= MAX_SUBSCRIPTIONS) { + s = sm->subscriptions; + wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " + "trashing oldest"); + subscription_unlink(s); + subscription_destroy(s); + } + + s = os_zalloc(sizeof(*s)); + if (s == NULL) + return NULL; + + s->sm = sm; + s->timeout_time = expire; + uuid_make(s->uuid); + subscr_addr_list_create(s, callback_urls); + /* Add to end of list, since it has the highest expiration time */ + subscription_link_to_end(s); + /* Queue up immediate event message (our last event) + * as required by UPnP spec. + */ + if (subscription_first_event(s)) { + wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to " + "event backlog"); + subscription_unlink(s); + subscription_destroy(s); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", + s, callback_urls); + os_free(callback_urls); + /* Schedule sending this */ + event_send_all_later(sm); + return s; +} + + +/* subscription_renew -- find subscription and reset timeout */ +struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, + const u8 uuid[UUID_LEN]) +{ + time_t now = time(NULL); + time_t expire = now + UPNP_SUBSCRIBE_SEC; + struct subscription *s = subscription_find(sm, uuid); + if (s == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed"); + subscription_unlink(s); + s->timeout_time = expire; + /* add back to end of list, since it now has highest expiry */ + subscription_link_to_end(s); + return s; +} + + +/** + * upnp_wps_device_send_wlan_event - Event notification + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @from_mac_addr: Source (Enrollee) MAC address for the event + * @ev_type: Event type + * @msg: Event data + * Returns: 0 on success, -1 on failure + * + * Tell external Registrars (UPnP control points) that something happened. In + * particular, events include WPS messages from clients that are proxied to + * external Registrars. + */ +int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, + const u8 from_mac_addr[ETH_ALEN], + enum upnp_wps_wlanevent_type ev_type, + const struct wpabuf *msg) +{ + int ret = -1; + char type[2]; + const u8 *mac = from_mac_addr; + char mac_text[18]; + u8 *raw = NULL; + size_t raw_len; + char *val; + size_t val_len; + int pos = 0; + + if (!sm) + goto fail; + + os_snprintf(type, sizeof(type), "%1u", ev_type); + + raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0); + raw = os_zalloc(raw_len); + if (!raw) + goto fail; + + *(raw + pos) = (u8) ev_type; + pos += 1; + os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac)); + wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s", + mac_text); + os_memcpy(raw + pos, mac_text, 17); + pos += 17; + if (msg) { + os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg)); + pos += wpabuf_len(msg); + } + raw_len = pos; + + val = (char *) base64_encode(raw, raw_len, &val_len); + if (val == NULL) + goto fail; + + os_free(sm->wlanevent); + sm->wlanevent = val; + upnp_wps_device_send_event(sm); + + ret = 0; + +fail: + os_free(raw); + + return ret; +} + + +/** + * get_netif_info - Get hw and IP addresses for network device + * @net_if: Selected network interface name + * @ip_addr: Buffer for returning IP address in network byte order + * @ip_addr_text: Buffer for returning a pointer to allocated IP address text + * @mac: Buffer for returning MAC address + * @mac_addr_text: Buffer for returning allocated MAC address text + * Returns: 0 on success, -1 on failure + */ +static int get_netif_info(const char *net_if, unsigned *ip_addr, + char **ip_addr_text, u8 mac[ETH_ALEN], + char **mac_addr_text) +{ + struct ifreq req; + int sock = -1; + struct sockaddr_in *addr; + struct in_addr in_addr; + + *ip_addr_text = os_zalloc(16); + *mac_addr_text = os_zalloc(18); + if (*ip_addr_text == NULL || *mac_addr_text == NULL) + goto fail; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + goto fail; + + os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); + if (ioctl(sock, SIOCGIFADDR, &req) < 0) { + wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)", + errno, strerror(errno)); + goto fail; + } + addr = (void *) &req.ifr_addr; + *ip_addr = addr->sin_addr.s_addr; + in_addr.s_addr = *ip_addr; + os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr)); + + os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); + if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) { + wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: " + "%d (%s)", errno, strerror(errno)); + goto fail; + } + os_memcpy(mac, req.ifr_addr.sa_data, 6); + os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data)); + + close(sock); + return 0; + +fail: + if (sock >= 0) + close(sock); + os_free(*ip_addr_text); + *ip_addr_text = NULL; + os_free(*mac_addr_text); + *mac_addr_text = NULL; + return -1; +} + + +/** + * upnp_wps_device_stop - Stop WPS UPnP operations on an interface + * @sm: WPS UPnP state machine from upnp_wps_device_init() + */ +void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) +{ + if (!sm || !sm->started) + return; + + wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); + web_listener_stop(sm); + while (sm->web_connections) + web_connection_stop(sm->web_connections); + while (sm->msearch_replies) + msearchreply_state_machine_stop(sm->msearch_replies); + while (sm->subscriptions) { + struct subscription *s = sm->subscriptions; + subscription_unlink(s); + subscription_destroy(s); + } + + advertisement_state_machine_stop(sm); + /* TODO: send byebye notifications */ + + event_send_stop_all(sm); + os_free(sm->wlanevent); + sm->wlanevent = NULL; + os_free(sm->net_if); + sm->net_if = NULL; + os_free(sm->mac_addr_text); + sm->mac_addr_text = NULL; + os_free(sm->ip_addr_text); + sm->ip_addr_text = NULL; + if (sm->multicast_sd >= 0) + close(sm->multicast_sd); + sm->multicast_sd = -1; + ssdp_listener_stop(sm); + + sm->started = 0; +} + + +/** + * upnp_wps_device_start - Start WPS UPnP operations on an interface + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @net_if: Selected network interface name + * Returns: 0 on success, -1 on failure + */ +int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) +{ + if (!sm || !net_if) + return -1; + + if (sm->started) + upnp_wps_device_stop(sm); + + sm->net_if = strdup(net_if); + sm->multicast_sd = -1; + sm->ssdp_sd = -1; + sm->started = 1; + sm->advertise_count = 0; + + /* Fix up linux multicast handling */ + if (add_ssdp_network(net_if)) + goto fail; + + /* Determine which IP and mac address we're using */ + if (get_netif_info(net_if, + &sm->ip_addr, &sm->ip_addr_text, + sm->mac_addr, &sm->mac_addr_text)) { + wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " + "for %s. Does it have IP address?", net_if); + goto fail; + } + + /* Listen for incoming TCP connections so that others + * can fetch our "xml files" from us. + */ + if (web_listener_start(sm)) + goto fail; + + /* Set up for receiving discovery (UDP) packets */ + if (ssdp_listener_start(sm)) + goto fail; + + /* Set up for sending multicast */ + if (ssdp_open_multicast(sm) < 0) + goto fail; + + /* + * Broadcast NOTIFY messages to let the world know we exist. + * This is done via a state machine since the messages should not be + * all sent out at once. + */ + if (advertisement_state_machine_start(sm)) + goto fail; + + return 0; + +fail: + upnp_wps_device_stop(sm); + return -1; +} + + +/** + * upnp_wps_device_deinit - Deinitialize WPS UPnP + * @sm: WPS UPnP state machine from upnp_wps_device_init() + */ +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) +{ + if (!sm) + return; + + upnp_wps_device_stop(sm); + + if (sm->peer.wps) + wps_deinit(sm->peer.wps); + os_free(sm->root_dir); + os_free(sm->desc_url); + os_free(sm->ctx); + os_free(sm); +} + + +/** + * upnp_wps_device_init - Initialize WPS UPnP + * @ctx: callback table; we must eventually free it + * @wps: Pointer to longterm WPS context + * @priv: External context data that will be used in callbacks + * Returns: WPS UPnP state or %NULL on failure + */ +struct upnp_wps_device_sm * +upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, + void *priv) +{ + struct upnp_wps_device_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (!sm) { + wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed"); + return NULL; + } + + sm->ctx = ctx; + sm->wps = wps; + sm->priv = priv; + + return sm; +} + + +/** + * upnp_wps_subscribers - Check whether there are any event subscribers + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 if no subscribers, 1 if subscribers + */ +int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) +{ + return sm->subscriptions != NULL; +} diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h new file mode 100644 index 0000000000000..31b055628bd6b --- /dev/null +++ b/src/wps/wps_upnp.h @@ -0,0 +1,67 @@ +/* + * UPnP WPS Device + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#ifndef WPS_UPNP_H +#define WPS_UPNP_H + +struct upnp_wps_device_sm; +struct wps_context; +struct wps_data; + +struct upnp_wps_peer { + struct wps_data *wps; +}; + +enum upnp_wps_wlanevent_type { + UPNP_WPS_WLANEVENT_TYPE_PROBE = 1, + UPNP_WPS_WLANEVENT_TYPE_EAP = 2 +}; + +struct upnp_wps_device_ctx { + struct wpabuf * (*rx_req_get_device_info)( + void *priv, struct upnp_wps_peer *peer); + struct wpabuf * (*rx_req_put_message)( + void *priv, struct upnp_wps_peer *peer, + const struct wpabuf *msg); + struct wpabuf * (*rx_req_get_ap_settings)(void *priv, + const struct wpabuf *msg); + int (*rx_req_set_ap_settings)(void *priv, const struct wpabuf *msg); + int (*rx_req_del_ap_settings)(void *priv, const struct wpabuf *msg); + struct wpabuf * (*rx_req_get_sta_settings)(void *priv, + const struct wpabuf *msg); + int (*rx_req_set_sta_settings)(void *priv, const struct wpabuf *msg); + int (*rx_req_del_sta_settings)(void *priv, const struct wpabuf *msg); + int (*rx_req_put_wlan_response)( + void *priv, enum upnp_wps_wlanevent_type ev_type, + const u8 *mac_addr, const struct wpabuf *msg, + enum wps_msg_type msg_type); + int (*rx_req_set_selected_registrar)(void *priv, + const struct wpabuf *msg); + int (*rx_req_reboot_ap)(void *priv, const struct wpabuf *msg); + int (*rx_req_reset_ap)(void *priv, const struct wpabuf *msg); + int (*rx_req_reboot_sta)(void *priv, const struct wpabuf *msg); + int (*rx_req_reset_sta)(void *priv, const struct wpabuf *msg); +}; + +struct upnp_wps_device_sm * +upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, + void *priv); +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm); + +int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if); +void upnp_wps_device_stop(struct upnp_wps_device_sm *sm); + +int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, + const u8 from_mac_addr[ETH_ALEN], + enum upnp_wps_wlanevent_type ev_type, + const struct wpabuf *msg); +int upnp_wps_subscribers(struct upnp_wps_device_sm *sm); + +#endif /* WPS_UPNP_H */ diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c new file mode 100644 index 0000000000000..f278f35bddd08 --- /dev/null +++ b/src/wps/wps_upnp_event.c @@ -0,0 +1,534 @@ +/* + * UPnP WPS Device - Event processing + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#include "includes.h" +#include <assert.h> +#include <fcntl.h> + +#include "common.h" +#include "eloop.h" +#include "uuid.h" +#include "httpread.h" +#include "wps_defs.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + +/* + * Event message generation (to subscribers) + * + * We make a separate copy for each message for each subscriber. This memory + * wasted could be limited (adding code complexity) by sharing copies, keeping + * a usage count and freeing when zero. + * + * Sending a message requires using a HTTP over TCP NOTIFY + * (like a PUT) which requires a number of states.. + */ + +#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */ +#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */ + +/* How long to wait before sending event */ +#define EVENT_DELAY_SECONDS 0 +#define EVENT_DELAY_MSEC 0 + +/* + * Event information that we send to each subscriber is remembered in this + * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP + * over TCP transaction which requires various states.. It may also need to be + * retried at a different address (if more than one is available). + * + * TODO: As an optimization we could share data between subscribers. + */ +struct wps_event_ { + struct wps_event_ *next; + struct wps_event_ *prev; /* double linked list */ + struct subscription *s; /* parent */ + unsigned subscriber_sequence; /* which event for this subscription*/ + int retry; /* which retry */ + struct subscr_addr *addr; /* address to connect to */ + struct wpabuf *data; /* event data to send */ + /* The following apply while we are sending an event message. */ + int sd; /* -1 or socket descriptor for open connection */ + int sd_registered; /* nonzero if we must cancel registration */ + struct httpread *hread; /* NULL or open connection for event msg */ +}; + + +static void event_timeout_handler(void *eloop_data, void *user_ctx); + +/* event_clean -- clean sockets etc. of event + * Leaves data, retry count etc. alone. + */ +static void event_clean(struct wps_event_ *e) +{ + if (e->s->current_event == e) { + eloop_cancel_timeout(event_timeout_handler, NULL, e); + e->s->current_event = NULL; + } + if (e->sd_registered) { + eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); + e->sd_registered = 0; + } + if (e->sd != -1) { + close(e->sd); + e->sd = -1; + } + if (e->hread) + httpread_destroy(e->hread); + e->hread = NULL; +} + + +/* event_delete -- delete single unqueued event + * (be sure to dequeue first if need be) + */ +void event_delete(struct wps_event_ *e) +{ + event_clean(e); + wpabuf_free(e->data); + os_free(e); +} + + +/* event_dequeue -- get next event from the queue + * Returns NULL if empty. + */ +static struct wps_event_ *event_dequeue(struct subscription *s) +{ + struct wps_event_ **event_head = &s->event_queue; + struct wps_event_ *e = *event_head; + if (e == NULL) + return NULL; + e->next->prev = e->prev; + e->prev->next = e->next; + if (*event_head == e) { + if (e == e->next) { + /* last in queue */ + *event_head = NULL; + } else { + *event_head = e->next; + } + } + s->n_queue--; + e->next = e->prev = NULL; + /* but parent "s" is still valid */ + return e; +} + + +/* event_enqueue_at_end -- add event to end of queue */ +static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e) +{ + struct wps_event_ **event_head = &s->event_queue; + if (*event_head == NULL) { + *event_head = e->next = e->prev = e; + } else { + e->next = *event_head; + e->prev = e->next->prev; + e->prev->next = e; + e->next->prev = e; + } + s->n_queue++; +} + + +/* event_enqueue_at_begin -- add event to begin of queue + * (appropriate for retrying event only) + */ +static void event_enqueue_at_begin(struct subscription *s, + struct wps_event_ *e) +{ + struct wps_event_ **event_head = &s->event_queue; + if (*event_head == NULL) { + *event_head = e->next = e->prev = e; + } else { + e->prev = *event_head; + e->next = e->prev->next; + e->prev->next = e; + e->next->prev = e; + *event_head = e; + } + s->n_queue++; +} + + +/* event_delete_all -- delete entire event queue and current event */ +void event_delete_all(struct subscription *s) +{ + struct wps_event_ *e; + while ((e = event_dequeue(s)) != NULL) + event_delete(e); + if (s->current_event) { + event_delete(s->current_event); + /* will set: s->current_event = NULL; */ + } +} + + +/** + * event_retry - Called when we had a failure delivering event msg + * @e: Event + * @do_next_address: skip address e.g. on connect fail + */ +static void event_retry(struct wps_event_ *e, int do_next_address) +{ + struct subscription *s = e->s; + struct upnp_wps_device_sm *sm = s->sm; + + event_clean(e); + /* will set: s->current_event = NULL; */ + + if (do_next_address) + e->retry++; + if (e->retry >= s->n_addr) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " + "for %s", e->addr->domain_and_port); + return; + } + event_enqueue_at_begin(s, e); + event_send_all_later(sm); +} + + +/* called if the overall event-sending process takes too long */ +static void event_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct wps_event_ *e = user_ctx; + struct subscription *s = e->s; + + assert(e == s->current_event); + + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); + event_retry(e, 1); +} + + +/* event_got_response_handler -- called back when http response is received. */ +static void event_got_response_handler(struct httpread *handle, void *cookie, + enum httpread_event en) +{ + struct wps_event_ *e = cookie; + struct subscription *s = e->s; + struct upnp_wps_device_sm *sm = s->sm; + struct httpread *hread = e->hread; + int reply_code = 0; + + assert(e == s->current_event); + eloop_cancel_timeout(event_timeout_handler, NULL, e); + + if (en == HTTPREAD_EVENT_FILE_READY) { + if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) { + reply_code = httpread_reply_code_get(hread); + if (reply_code == HTTP_OK) { + wpa_printf(MSG_DEBUG, + "WPS UPnP: Got event reply OK from " + "%s", e->addr->domain_and_port); + event_delete(e); + goto send_more; + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Got event " + "error reply code %d from %s", + reply_code, + e->addr->domain_and_port); + goto bad; + } + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event " + "response %d from %s", en, + e->addr->domain_and_port); + } + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail " + "for %s", e->addr->domain_and_port); + goto bad; + } + event_retry(e, 1); + goto send_more; + +send_more: + /* Schedule sending more if there is more to send */ + if (s->event_queue) + event_send_all_later(sm); + return; + +bad: + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping + * them...). + * Alternately, we could just do event_delete(e) + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors"); + subscription_unlink(s); + subscription_destroy(s); +} + + +/* event_send_tx_ready -- actually write event message + * + * Prequisite: subscription socket descriptor has become ready to + * write (because connection to subscriber has been made). + * + * It is also possible that we are called because the connect has failed; + * it is possible to test for this, or we can just go ahead and then + * the write will fail. + */ +static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wps_event_ *e = sock_ctx; + struct subscription *s = e->s; + struct wpabuf *buf; + char *b; + + assert(e == s->current_event); + assert(e->sd == sock); + + buf = wpabuf_alloc(1000 + wpabuf_len(e->data)); + if (buf == NULL) { + event_retry(e, 0); + goto bad; + } + wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path); + wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); + wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port); + wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" + "NT: upnp:event\r\n" + "NTS: upnp:propchange\r\n"); + wpabuf_put_str(buf, "SID: uuid:"); + b = wpabuf_put(buf, 0); + uuid_bin2str(s->uuid, b, 80); + wpabuf_put(buf, os_strlen(b)); + wpabuf_put_str(buf, "\r\n"); + wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence); + wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n", + (int) wpabuf_len(e->data)); + wpabuf_put_str(buf, "\r\n"); /* terminating empty line */ + wpabuf_put_buf(buf, e->data); + + /* Since the message size is pretty small, we should be + * able to get the operating system to buffer what we give it + * and not have to come back again later to write more... + */ +#if 0 + /* we could: Turn blocking back on? */ + fcntl(e->sd, F_SETFL, 0); +#endif + wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s", + e->addr->domain_and_port); + if (send_wpabuf(e->sd, buf) < 0) { + event_retry(e, 1); + goto bad; + } + wpabuf_free(buf); + buf = NULL; + + if (e->sd_registered) { + e->sd_registered = 0; + eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); + } + /* Set up to read the reply */ + e->hread = httpread_create(e->sd, event_got_response_handler, + e /* cookie */, + 0 /* no data expected */, + EVENT_TIMEOUT_SEC); + if (e->hread == NULL) { + wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed"); + event_retry(e, 0); + goto bad; + } + return; + +bad: + /* Schedule sending more if there is more to send */ + if (s->event_queue) + event_send_all_later(s->sm); + wpabuf_free(buf); +} + + +/* event_send_start -- prepare to send a event message to subscriber + * + * This gets complicated because: + * -- The message is sent via TCP and we have to keep the stream open + * for 30 seconds to get a response... then close it. + * -- But we might have other event happen in the meantime... + * we have to queue them, if we lose them then the subscriber will + * be forced to unsubscribe and subscribe again. + * -- If multiple URLs are provided then we are supposed to try successive + * ones after 30 second timeout. + * -- The URLs might use domain names instead of dotted decimal addresses, + * and resolution of those may cause unwanted sleeping. + * -- Doing the initial TCP connect can take a while, so we have to come + * back after connection and then send the data. + * + * Returns nonzero on error; + * + * Prerequisite: No current event send (s->current_event == NULL) + * and non-empty queue. + */ +static int event_send_start(struct subscription *s) +{ + struct wps_event_ *e; + int itry; + + /* + * Assume we are called ONLY with no current event and ONLY with + * nonempty event queue and ONLY with at least one address to send to. + */ + assert(s->addr_list != NULL); + assert(s->current_event == NULL); + assert(s->event_queue != NULL); + + s->current_event = e = event_dequeue(s); + + /* Use address acc. to no. of retries */ + e->addr = s->addr_list; + for (itry = 0; itry < e->retry; itry++) + e->addr = e->addr->next; + + e->sd = socket(AF_INET, SOCK_STREAM, 0); + if (e->sd < 0) { + event_retry(e, 0); + return -1; + } + /* set non-blocking so we don't sleep waiting for connection */ + if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) { + event_retry(e, 0); + return -1; + } + /* + * Start the connect. It might succeed immediately but more likely will + * return errno EINPROGRESS. + */ + if (connect(e->sd, (struct sockaddr *) &e->addr->saddr, + sizeof(e->addr->saddr))) { + if (errno != EINPROGRESS) { + event_retry(e, 1); + return -1; + } + } + /* Call back when ready for writing (or on failure...). */ + if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready, + NULL, e)) { + event_retry(e, 0); + return -1; + } + e->sd_registered = 1; + /* Don't wait forever! */ + if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler, + NULL, e)) { + event_retry(e, 0); + return -1; + } + return 0; +} + + +/* event_send_all_later_handler -- actually send events as needed */ +void event_send_all_later_handler(void *eloop_data, void *user_ctx) +{ + struct upnp_wps_device_sm *sm = user_ctx; + struct subscription *s; + struct subscription *s_old; + int nerrors = 0; + + sm->event_send_all_queued = 0; + s = sm->subscriptions; + if (s == NULL) + return; + do { + if (s->addr_list == NULL) { + /* if we've given up on all addresses */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " + "subscription with no addresses"); + s_old = s; + s = s_old->next; + subscription_unlink(s_old); + subscription_destroy(s_old); + } else { + if (s->current_event == NULL /* not busy */ && + s->event_queue != NULL /* more to do */) { + if (event_send_start(s)) + nerrors++; + } + s = s->next; + } + } while (sm->subscriptions != NULL && s != sm->subscriptions); + + if (nerrors) { + /* Try again later */ + event_send_all_later(sm); + } +} + + +/* event_send_all_later -- schedule sending events to all subscribers + * that need it. + * This avoids two problems: + * -- After getting a subscription, we should not send the first event + * until after our reply is fully queued to be sent back, + * -- Possible stack depth or infinite recursion issues. + */ +void event_send_all_later(struct upnp_wps_device_sm *sm) +{ + /* + * The exact time in the future isn't too important. Waiting a bit + * might let us do several together. + */ + if (sm->event_send_all_queued) + return; + sm->event_send_all_queued = 1; + eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC, + event_send_all_later_handler, NULL, sm); +} + + +/* event_send_stop_all -- cleanup */ +void event_send_stop_all(struct upnp_wps_device_sm *sm) +{ + if (sm->event_send_all_queued) + eloop_cancel_timeout(event_send_all_later_handler, NULL, sm); + sm->event_send_all_queued = 0; +} + + +/** + * event_add - Add a new event to a queue + * @s: Subscription + * @data: Event data (is copied; caller retains ownership) + * Returns: 0 on success, 1 on error + */ +int event_add(struct subscription *s, const struct wpabuf *data) +{ + struct wps_event_ *e; + + if (s->n_queue >= MAX_EVENTS_QUEUED) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " + "subscriber"); + return 1; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return 1; + e->s = s; + e->sd = -1; + e->data = wpabuf_dup(data); + if (e->data == NULL) { + os_free(e); + return 1; + } + e->subscriber_sequence = s->next_subscriber_sequence++; + if (s->next_subscriber_sequence == 0) + s->next_subscriber_sequence++; + event_enqueue_at_end(s, e); + event_send_all_later(s->sm); + return 0; +} diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h new file mode 100644 index 0000000000000..d4b6569e19994 --- /dev/null +++ b/src/wps/wps_upnp_i.h @@ -0,0 +1,193 @@ +/* + * UPnP for WPS / internal definitions + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#ifndef WPS_UPNP_I_H +#define WPS_UPNP_I_H + +#define UPNP_MULTICAST_ADDRESS "239.255.255.250" /* for UPnP multicasting */ +#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */ + +/* min subscribe time per UPnP standard */ +#define UPNP_SUBSCRIBE_SEC_MIN 1800 +/* subscribe time we use */ +#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1) + +/* "filenames" used in URLs that we service via our "web server": */ +#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml" +#define UPNP_WPS_SCPD_XML_FILE "wps_scpd.xml" +#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control" +#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event" + + +struct web_connection; +struct subscription; +struct upnp_wps_device_sm; + + +enum http_reply_code { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + UPNP_INVALID_ACTION = 401, + UPNP_INVALID_ARGS = 402, + HTTP_PRECONDITION_FAILED = 412, + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_UNIMPLEMENTED = 501, + UPNP_ACTION_FAILED = 501, + UPNP_ARG_VALUE_INVALID = 600, + UPNP_ARG_VALUE_OUT_OF_RANGE = 601, + UPNP_OUT_OF_MEMORY = 603 +}; + + +enum advertisement_type_enum { + ADVERTISE_UP = 0, + ADVERTISE_DOWN = 1, + MSEARCH_REPLY = 2 +}; + +/* + * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of + * the reply to UDP M-SEARCH requests. This struct handles both cases. + * + * A state machine is needed because a number of variant forms must be sent in + * separate packets and spread out in time to avoid congestion. + */ +struct advertisement_state_machine { + /* double-linked list */ + struct advertisement_state_machine *next; + struct advertisement_state_machine *prev; + struct upnp_wps_device_sm *sm; /* parent */ + enum advertisement_type_enum type; + int state; + int nerrors; + struct sockaddr_in client; /* for M-SEARCH replies */ +}; + + +/* + * An address of a subscriber (who may have multiple addresses). We are + * supposed to send (via TCP) updates to each subscriber, trying each address + * for a subscriber until we find one that seems to work. + */ +struct subscr_addr { + /* double linked list */ + struct subscr_addr *next; + struct subscr_addr *prev; + struct subscription *s; /* parent */ + char *domain_and_port; /* domain and port part of url */ + char *path; /* "filepath" part of url (from "mem") */ + struct sockaddr_in saddr; /* address for doing connect */ +}; + + +/* + * Subscribers to our events are recorded in this struct. This includes a max + * of one outgoing connection (sending an "event message") per subscriber. We + * also have to age out subscribers unless they renew. + */ +struct subscription { + /* double linked list */ + struct subscription *next; + struct subscription *prev; + struct upnp_wps_device_sm *sm; /* parent */ + time_t timeout_time; /* when to age out the subscription */ + unsigned next_subscriber_sequence; /* number our messages */ + /* + * This uuid identifies the subscription and is randomly generated by + * us and given to the subscriber when the subscription is accepted; + * and is then included with each event sent to the subscriber. + */ + u8 uuid[UUID_LEN]; + /* Linked list of address alternatives (rotate through on failure) */ + struct subscr_addr *addr_list; + int n_addr; /* Number of addresses in list */ + struct wps_event_ *event_queue; /* Queued event messages. */ + int n_queue; /* How many events are queued */ + struct wps_event_ *current_event; /* non-NULL if being sent (not in q) + */ +}; + + +/* + * Our instance data corresponding to one WiFi network interface + * (multiple might share the same wired network interface!). + * + * This is known as an opaque struct declaration to users of the WPS UPnP code. + */ +struct upnp_wps_device_sm { + struct upnp_wps_device_ctx *ctx; /* callback table */ + struct wps_context *wps; + void *priv; + char *root_dir; + char *desc_url; + int started; /* nonzero if we are active */ + char *net_if; /* network interface we use */ + char *mac_addr_text; /* mac addr of network i.f. we use */ + u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */ + char *ip_addr_text; /* IP address of network i.f. we use */ + unsigned ip_addr; /* IP address of network i.f. we use (host order) */ + int multicast_sd; /* send multicast messages over this socket */ + int ssdp_sd; /* receive discovery UPD packets on socket */ + int ssdp_sd_registered; /* nonzero if we must unregister */ + unsigned advertise_count; /* how many advertisements done */ + struct advertisement_state_machine advertisement; + struct advertisement_state_machine *msearch_replies; + int n_msearch_replies; /* no. of pending M-SEARCH replies */ + int web_port; /* our port that others get xml files from */ + int web_sd; /* socket to listen for web requests */ + int web_sd_registered; /* nonzero if we must cancel registration */ + struct web_connection *web_connections; /* linked list */ + int n_web_connections; /* no. of pending web connections */ + /* Note: subscriptions are kept in expiry order */ + struct subscription *subscriptions; /* linked list */ + int n_subscriptions; /* no of current subscriptions */ + int event_send_all_queued; /* if we are scheduled to send events soon + */ + + char *wlanevent; /* the last WLANEvent data */ + + /* FIX: maintain separate structures for each UPnP peer */ + struct upnp_wps_peer peer; +}; + +/* wps_upnp.c */ +void format_date(struct wpabuf *buf); +struct subscription * subscription_start(struct upnp_wps_device_sm *sm, + char *callback_urls); +struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, + const u8 uuid[UUID_LEN]); +void subscription_unlink(struct subscription *s); +void subscription_destroy(struct subscription *s); +struct subscription * subscription_find(struct upnp_wps_device_sm *sm, + const u8 uuid[UUID_LEN]); +int send_wpabuf(int fd, struct wpabuf *buf); + +/* wps_upnp_ssdp.c */ +void msearchreply_state_machine_stop(struct advertisement_state_machine *a); +int advertisement_state_machine_start(struct upnp_wps_device_sm *sm); +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm); +void ssdp_listener_stop(struct upnp_wps_device_sm *sm); +int ssdp_listener_start(struct upnp_wps_device_sm *sm); +int add_ssdp_network(char *net_if); +int ssdp_open_multicast(struct upnp_wps_device_sm *sm); + +/* wps_upnp_web.c */ +void web_connection_stop(struct web_connection *c); +int web_listener_start(struct upnp_wps_device_sm *sm); +void web_listener_stop(struct upnp_wps_device_sm *sm); + +/* wps_upnp_event.c */ +int event_add(struct subscription *s, const struct wpabuf *data); +void event_delete(struct wps_event_ *e); +void event_delete_all(struct subscription *s); +void event_send_all_later(struct upnp_wps_device_sm *sm); +void event_send_stop_all(struct upnp_wps_device_sm *sm); + +#endif /* WPS_UPNP_I_H */ diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c new file mode 100644 index 0000000000000..6a94c592e34ac --- /dev/null +++ b/src/wps/wps_upnp_ssdp.c @@ -0,0 +1,886 @@ +/* + * UPnP SSDP for WPS + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#include "includes.h" + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <net/route.h> + +#include "common.h" +#include "uuid.h" +#include "eloop.h" +#include "wps.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + +#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */ +#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */ +#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */ +#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */ +#define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */ +#define SSDP_TARGET "239.0.0.0" +#define SSDP_NETMASK "255.0.0.0" + + +/* Check tokens for equality, where tokens consist of letters, digits, + * underscore and hyphen, and are matched case insensitive. + */ +static int token_eq(const char *s1, const char *s2) +{ + int c1; + int c2; + int end1 = 0; + int end2 = 0; + for (;;) { + c1 = *s1++; + c2 = *s2++; + if (isalpha(c1) && isupper(c1)) + c1 = tolower(c1); + if (isalpha(c2) && isupper(c2)) + c2 = tolower(c2); + end1 = !(isalnum(c1) || c1 == '_' || c1 == '-'); + end2 = !(isalnum(c2) || c2 == '_' || c2 == '-'); + if (end1 || end2 || c1 != c2) + break; + } + return end1 && end2; /* reached end of both words? */ +} + + +/* Return length of token (see above for definition of token) */ +static int token_length(const char *s) +{ + const char *begin = s; + for (;; s++) { + int c = *s; + int end = !(isalnum(c) || c == '_' || c == '-'); + if (end) + break; + } + return s - begin; +} + + +/* return length of interword separation. + * This accepts only spaces/tabs and thus will not traverse a line + * or buffer ending. + */ +static int word_separation_length(const char *s) +{ + const char *begin = s; + for (;; s++) { + int c = *s; + if (c == ' ' || c == '\t') + continue; + break; + } + return s - begin; +} + + +/* No. of chars through (including) end of line */ +static int line_length(const char *l) +{ + const char *lp = l; + while (*lp && *lp != '\n') + lp++; + if (*lp == '\n') + lp++; + return lp - l; +} + + +/* No. of chars excluding trailing whitespace */ +static int line_length_stripped(const char *l) +{ + const char *lp = l + line_length(l); + while (lp > l && !isgraph(lp[-1])) + lp--; + return lp - l; +} + + +static int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} + + +/*************************************************************************** + * Advertisements. + * These are multicast to the world to tell them we are here. + * The individual packets are spread out in time to limit loss, + * and then after a much longer period of time the whole sequence + * is repeated again (for NOTIFYs only). + **************************************************************************/ + +/** + * next_advertisement - Build next message and advance the state machine + * @a: Advertisement state + * @islast: Buffer for indicating whether this is the last message (= 1) + * Returns: The new message (caller is responsible for freeing this) + * + * Note: next_advertisement is shared code with msearchreply_* functions + */ +static struct wpabuf * +next_advertisement(struct advertisement_state_machine *a, int *islast) +{ + struct wpabuf *msg; + char *NTString = ""; + char uuid_string[80]; + + *islast = 0; + uuid_bin2str(a->sm->wps->uuid, uuid_string, sizeof(uuid_string)); + msg = wpabuf_alloc(800); /* more than big enough */ + if (msg == NULL) + goto fail; + switch (a->type) { + case ADVERTISE_UP: + case ADVERTISE_DOWN: + NTString = "NT"; + wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n"); + wpabuf_printf(msg, "HOST: %s:%d\r\n", + UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT); + wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n", + UPNP_CACHE_SEC); + wpabuf_printf(msg, "NTS: %s\r\n", + (a->type == ADVERTISE_UP ? + "ssdp:alive" : "ssdp:byebye")); + break; + case MSEARCH_REPLY: + NTString = "ST"; + wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n"); + wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n", + UPNP_CACHE_SEC); + + wpabuf_put_str(msg, "DATE: "); + format_date(msg); + wpabuf_put_str(msg, "\r\n"); + + wpabuf_put_str(msg, "EXT:\r\n"); + break; + } + + if (a->type != ADVERTISE_DOWN) { + /* Where others may get our XML files from */ + wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n", + a->sm->ip_addr_text, a->sm->web_port, + UPNP_WPS_DEVICE_XML_FILE); + } + + /* The SERVER line has three comma-separated fields: + * operating system / version + * upnp version + * software package / version + * However, only the UPnP version is really required, the + * others can be place holders... for security reasons + * it is better to NOT provide extra information. + */ + wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); + + switch (a->state / UPNP_ADVERTISE_REPEAT) { + case 0: + wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString); + wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n", + uuid_string); + break; + case 1: + wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string); + wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string); + break; + case 2: + wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:" + "WFADevice:1\r\n", NTString); + wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-" + "org:device:WFADevice:1\r\n", uuid_string); + break; + case 3: + wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:" + "WFAWLANConfig:1\r\n", NTString); + wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-" + "org:service:WFAWLANConfig:1\r\n", uuid_string); + break; + } + wpabuf_put_str(msg, "\r\n"); + + if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT) + *islast = 1; + + return msg; + +fail: + wpabuf_free(msg); + return NULL; +} + + +static void advertisement_state_machine_handler(void *eloop_data, + void *user_ctx); + + +/** + * advertisement_state_machine_stop - Stop SSDP advertisements + * @sm: WPS UPnP state machine from upnp_wps_device_init() + */ +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm) +{ + eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm); +} + + +static void advertisement_state_machine_handler(void *eloop_data, + void *user_ctx) +{ + struct upnp_wps_device_sm *sm = user_ctx; + struct advertisement_state_machine *a = &sm->advertisement; + struct wpabuf *msg; + int next_timeout_msec = 100; + int next_timeout_sec = 0; + struct sockaddr_in dest; + int islast = 0; + + /* + * Each is sent twice (in case lost) w/ 100 msec delay between; + * spec says no more than 3 times. + * One pair for rootdevice, one pair for uuid, and a pair each for + * each of the two urns. + * The entire sequence must be repeated before cache control timeout + * (which is min 1800 seconds), + * recommend random portion of half of the advertised cache control age + * to ensure against loss... perhaps 1800/4 + rand*1800/4 ? + * Delay random interval < 100 msec prior to initial sending. + * TTL of 4 + */ + + wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state); + msg = next_advertisement(a, &islast); + if (msg == NULL) + return; + + os_memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + dest.sin_port = htons(UPNP_MULTICAST_PORT); + + if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0, + (struct sockaddr *) &dest, sizeof(dest)) == -1) { + wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:" + "%d (%s)", errno, strerror(errno)); + next_timeout_msec = 0; + next_timeout_sec = 10; /* ... later */ + } else if (islast) { + a->state = 0; /* wrap around */ + if (a->type == ADVERTISE_DOWN) { + wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP"); + a->type = ADVERTISE_UP; + /* do it all over again right away */ + } else { + u16 r; + /* + * Start over again after a long timeout + * (see notes above) + */ + next_timeout_msec = 0; + os_get_random((void *) &r, sizeof(r)); + next_timeout_sec = UPNP_CACHE_SEC / 4 + + (((UPNP_CACHE_SEC / 4) * r) >> 16); + sm->advertise_count++; + wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); " + "next in %d sec", + sm->advertise_count, next_timeout_sec); + } + } else { + a->state++; + } + + wpabuf_free(msg); + + eloop_register_timeout(next_timeout_sec, next_timeout_msec, + advertisement_state_machine_handler, NULL, sm); +} + + +/** + * advertisement_state_machine_start - Start SSDP advertisements + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 on success, -1 on failure + */ +int advertisement_state_machine_start(struct upnp_wps_device_sm *sm) +{ + struct advertisement_state_machine *a = &sm->advertisement; + int next_timeout_msec; + + advertisement_state_machine_stop(sm); + + /* + * Start out advertising down, this automatically switches + * to advertising up which signals our restart. + */ + a->type = ADVERTISE_DOWN; + a->state = 0; + a->sm = sm; + /* (other fields not used here) */ + + /* First timeout should be random interval < 100 msec */ + next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8; + return eloop_register_timeout(0, next_timeout_msec, + advertisement_state_machine_handler, + NULL, sm); +} + + +/*************************************************************************** + * M-SEARCH replies + * These are very similar to the multicast advertisements, with some + * small changes in data content; and they are sent (UDP) to a specific + * unicast address instead of multicast. + * They are sent in response to a UDP M-SEARCH packet. + **************************************************************************/ + +static void msearchreply_state_machine_handler(void *eloop_data, + void *user_ctx); + + +/** + * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine + * @a: Selected advertisement/reply state + */ +void msearchreply_state_machine_stop(struct advertisement_state_machine *a) +{ + struct upnp_wps_device_sm *sm = a->sm; + wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop"); + if (a->next == a) { + sm->msearch_replies = NULL; + } else { + if (sm->msearch_replies == a) + sm->msearch_replies = a->next; + a->next->prev = a->prev; + a->prev->next = a->next; + } + os_free(a); + sm->n_msearch_replies--; +} + + +static void msearchreply_state_machine_handler(void *eloop_data, + void *user_ctx) +{ + struct advertisement_state_machine *a = user_ctx; + struct upnp_wps_device_sm *sm = a->sm; + struct wpabuf *msg; + int next_timeout_msec = 100; + int next_timeout_sec = 0; + int islast = 0; + + /* + * Each response is sent twice (in case lost) w/ 100 msec delay + * between; spec says no more than 3 times. + * One pair for rootdevice, one pair for uuid, and a pair each for + * each of the two urns. + */ + + /* TODO: should only send the requested response types */ + + wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)", + a->state, inet_ntoa(a->client.sin_addr), + ntohs(a->client.sin_port)); + msg = next_advertisement(a, &islast); + if (msg == NULL) + return; + + /* + * Send it on the multicast socket to avoid having to set up another + * socket. + */ + if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0, + (struct sockaddr *) &a->client, sizeof(a->client)) < 0) { + wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto " + "errno %d (%s) for %s:%d", + errno, strerror(errno), + inet_ntoa(a->client.sin_addr), + ntohs(a->client.sin_port)); + /* Ignore error and hope for the best */ + } + wpabuf_free(msg); + if (islast) { + wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done"); + msearchreply_state_machine_stop(a); + return; + } + a->state++; + + wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec", + next_timeout_sec, next_timeout_msec); + eloop_register_timeout(next_timeout_sec, next_timeout_msec, + msearchreply_state_machine_handler, sm, a); +} + + +/** + * msearchreply_state_machine_start - Reply to M-SEARCH discovery request + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @client: Client address + * @mx: Maximum delay in seconds + * + * Use TTL of 4 (this was done when socket set up). + * A response should be given in randomized portion of min(MX,120) seconds + * + * UPnP-arch-DeviceArchitecture, 1.2.3: + * To be found, a device must send a UDP response to the source IP address and + * port that sent the request to the multicast channel. Devices respond if the + * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:" + * followed by a UUID that exactly matches one advertised by the device. + */ +static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, + struct sockaddr_in *client, + int mx) +{ + struct advertisement_state_machine *a; + int next_timeout_sec; + int next_timeout_msec; + + wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d " + "outstanding)", sm->n_msearch_replies); + if (sm->n_msearch_replies >= MAX_MSEARCH) { + wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding " + "M-SEARCH replies"); + return; + } + + a = os_zalloc(sizeof(*a)); + if (a == NULL) + return; + a->type = MSEARCH_REPLY; + a->state = 0; + a->sm = sm; + os_memcpy(&a->client, client, sizeof(client)); + /* Wait time depending on MX value */ + next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8; + next_timeout_sec = next_timeout_msec / 1000; + next_timeout_msec = next_timeout_msec % 1000; + if (eloop_register_timeout(next_timeout_sec, next_timeout_msec, + msearchreply_state_machine_handler, sm, + a)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + /* Remember for future cleanup */ + if (sm->msearch_replies) { + a->next = sm->msearch_replies; + a->prev = a->next->prev; + a->prev->next = a; + a->next->prev = a; + } else { + sm->msearch_replies = a->next = a->prev = a; + } + sm->n_msearch_replies++; + return; + +fail: + wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!"); + eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a); + os_free(a); +} + + +/** + * ssdp_parse_msearch - Process a received M-SEARCH + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @client: Client address + * @data: NULL terminated M-SEARCH message + * + * Given that we have received a header w/ M-SEARCH, act upon it + * + * Format of M-SEARCH (case insensitive!): + * + * First line must be: + * M-SEARCH * HTTP/1.1 + * Other lines in arbitrary order: + * HOST:239.255.255.250:1900 + * ST:<varies -- must match> + * MAN:"ssdp:discover" + * MX:<varies> + * + * It should be noted that when Microsoft Vista is still learning its IP + * address, it sends out host lines like: HOST:[FF02::C]:1900 + */ +static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, + struct sockaddr_in *client, const char *data) +{ + const char *start = data; + const char *end; + int got_host = 0; + int got_st = 0, st_match = 0; + int got_man = 0; + int got_mx = 0; + int mx = 0; + + /* + * Skip first line M-SEARCH * HTTP/1.1 + * (perhaps we should check remainder of the line for syntax) + */ + data += line_length(data); + + /* Parse remaining lines */ + for (; *data != '\0'; data += line_length(data)) { + end = data + line_length_stripped(data); + if (token_eq(data, "host")) { + /* The host line indicates who the packet + * is addressed to... but do we really care? + * Note that Microsoft sometimes does funny + * stuff with the HOST: line. + */ +#if 0 /* could be */ + data += token_length(data); + data += word_separation_length(data); + if (*data != ':') + goto bad; + data++; + data += word_separation_length(data); + /* UPNP_MULTICAST_ADDRESS */ + if (!str_starts(data, "239.255.255.250")) + goto bad; + data += os_strlen("239.255.255.250"); + if (*data == ':') { + if (!str_starts(data, ":1900")) + goto bad; + } +#endif /* could be */ + got_host = 1; + continue; + } else if (token_eq(data, "st")) { + /* There are a number of forms; we look + * for one that matches our case. + */ + got_st = 1; + data += token_length(data); + data += word_separation_length(data); + if (*data != ':') + continue; + data++; + data += word_separation_length(data); + if (str_starts(data, "ssdp:all")) { + st_match = 1; + continue; + } + if (str_starts(data, "upnp:rootdevice")) { + st_match = 1; + continue; + } + if (str_starts(data, "uuid:")) { + char uuid_string[80]; + data += os_strlen("uuid:"); + uuid_bin2str(sm->wps->uuid, uuid_string, + sizeof(uuid_string)); + if (str_starts(data, uuid_string)) + st_match = 1; + continue; + } +#if 0 + /* FIX: should we really reply to IGD string? */ + if (str_starts(data, "urn:schemas-upnp-org:device:" + "InternetGatewayDevice:1")) { + st_match = 1; + continue; + } +#endif + if (str_starts(data, "urn:schemas-wifialliance-org:" + "service:WFAWLANConfig:1")) { + st_match = 1; + continue; + } + if (str_starts(data, "urn:schemas-wifialliance-org:" + "device:WFADevice:1")) { + st_match = 1; + continue; + } + continue; + } else if (token_eq(data, "man")) { + data += token_length(data); + data += word_separation_length(data); + if (*data != ':') + continue; + data++; + data += word_separation_length(data); + if (!str_starts(data, "\"ssdp:discover\"")) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected " + "M-SEARCH man-field"); + goto bad; + } + got_man = 1; + continue; + } else if (token_eq(data, "mx")) { + data += token_length(data); + data += word_separation_length(data); + if (*data != ':') + continue; + data++; + data += word_separation_length(data); + mx = atol(data); + got_mx = 1; + continue; + } + /* ignore anything else */ + } + if (!got_host || !got_st || !got_man || !got_mx || mx < 0) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d " + "%d mx=%d", got_host, got_st, got_man, got_mx, mx); + goto bad; + } + if (!st_match) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST " + "match)"); + return; + } + if (mx > 120) + mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */ + msearchreply_state_machine_start(sm, client, mx); + return; + +bad: + wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH"); + wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start); +} + + +/* Listening for (UDP) discovery (M-SEARCH) packets */ + +/** + * ssdp_listener_stop - Stop SSDP listered + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * + * This function stops the SSDP listerner that was started by calling + * ssdp_listener_start(). + */ +void ssdp_listener_stop(struct upnp_wps_device_sm *sm) +{ + if (sm->ssdp_sd_registered) { + eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ); + sm->ssdp_sd_registered = 0; + } + + if (sm->ssdp_sd != -1) { + close(sm->ssdp_sd); + sm->ssdp_sd = -1; + } + + eloop_cancel_timeout(msearchreply_state_machine_handler, sm, + ELOOP_ALL_CTX); +} + + +static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct upnp_wps_device_sm *sm = sock_ctx; + struct sockaddr_in addr; /* client address */ + socklen_t addr_len; + int nread; + char buf[MULTICAST_MAX_READ], *pos; + + addr_len = sizeof(addr); + nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &addr, &addr_len); + if (nread <= 0) + return; + buf[nread] = '\0'; /* need null termination for algorithm */ + + if (str_starts(buf, "NOTIFY ")) { + /* + * Silently ignore NOTIFYs to avoid filling debug log with + * unwanted messages. + */ + return; + } + + pos = os_strchr(buf, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: " + "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf); + if (pos) + *pos = '\n'; + + /* Parse first line */ + if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 && + !isgraph(buf[strlen("M-SEARCH")])) { + ssdp_parse_msearch(sm, &addr, buf); + return; + } + + /* Ignore anything else */ +} + + +/** + * ssdp_listener_start - Set up for receiving discovery (UDP) packets + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 on success, -1 on failure + * + * The SSDP listerner is stopped by calling ssdp_listener_stop(). + */ +int ssdp_listener_start(struct upnp_wps_device_sm *sm) +{ + int sd = -1; + struct sockaddr_in addr; + struct ip_mreq mcast_addr; + int on = 1; + /* per UPnP spec, keep IP packet time to live (TTL) small */ + unsigned char ttl = 4; + + sm->ssdp_sd = sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) + goto fail; + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + goto fail; + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + goto fail; + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(UPNP_MULTICAST_PORT); + if (bind(sd, (struct sockaddr *) &addr, sizeof(addr))) + goto fail; + os_memset(&mcast_addr, 0, sizeof(mcast_addr)); + mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY); + mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *) &mcast_addr, sizeof(mcast_addr))) + goto fail; + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl))) + goto fail; + if (eloop_register_sock(sd, EVENT_TYPE_READ, ssdp_listener_handler, + NULL, sm)) + goto fail; + sm->ssdp_sd_registered = 1; + return 0; + +fail: + /* Error */ + wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed"); + ssdp_listener_stop(sm); + return -1; +} + + +/** + * add_ssdp_network - Add routing entry for SSDP + * @net_if: Selected network interface name + * Returns: 0 on success, -1 on failure + * + * This function assures that the multicast address will be properly + * handled by Linux networking code (by a modification to routing tables). + * This must be done per network interface. It really only needs to be done + * once after booting up, but it does not hurt to call this more frequently + * "to be safe". + */ +int add_ssdp_network(char *net_if) +{ + int ret = -1; + int sock = -1; + struct rtentry rt; + struct sockaddr_in *sin; + + if (!net_if) + goto fail; + + os_memset(&rt, 0, sizeof(rt)); + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + goto fail; + + rt.rt_dev = net_if; + sin = (struct sockaddr_in *) &rt.rt_dst; + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(SSDP_TARGET); + sin = (struct sockaddr_in *) &rt.rt_genmask; + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK); + rt.rt_flags = RTF_UP; + if (ioctl(sock, SIOCADDRT, &rt) < 0) { + if (errno == EPERM) { + wpa_printf(MSG_DEBUG, "add_ssdp_network: No " + "permissions to add routing table entry"); + /* Continue to allow testing as non-root */ + } else if (errno != EEXIST) { + wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno " + "%d (%s)", errno, strerror(errno)); + goto fail; + } + } + + ret = 0; + +fail: + if (sock >= 0) + close(sock); + + return ret; +} + + +/** + * ssdp_open_multicast - Open socket for sending multicast SSDP messages + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 on success, -1 on failure + */ +int ssdp_open_multicast(struct upnp_wps_device_sm *sm) +{ + int sd = -1; + /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet + * time to live (TTL) small */ + unsigned char ttl = 4; + + sm->multicast_sd = sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) + return -1; + +#if 0 /* maybe ok if we sometimes block on writes */ + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + return -1; +#endif + + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, + &sm->ip_addr, sizeof(sm->ip_addr))) + return -1; + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl))) + return -1; + +#if 0 /* not needed, because we don't receive using multicast_sd */ + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + mreq.imr_interface.s_addr = sm->ip_addr; + wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr " + "0x%x", + mreq.imr_multiaddr.s_addr, + mreq.imr_interface.s_addr); + if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq))) { + wpa_printf(MSG_ERROR, + "WPS UPnP: setsockopt " + "IP_ADD_MEMBERSHIP errno %d (%s)", + errno, strerror(errno)); + return -1; + } + } +#endif /* not needed */ + + /* + * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default? + * which aids debugging I suppose but isn't really necessary? + */ + + return 0; +} diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c new file mode 100644 index 0000000000000..db47c29ead0f1 --- /dev/null +++ b/src/wps/wps_upnp_web.c @@ -0,0 +1,1964 @@ +/* + * UPnP WPS Device - Web connections + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#include "includes.h" +#include <fcntl.h> + +#include "common.h" +#include "base64.h" +#include "eloop.h" +#include "uuid.h" +#include "httpread.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + +/*************************************************************************** + * Web connections (we serve pages of info about ourselves, handle + * requests, etc. etc.). + **************************************************************************/ + +#define WEB_CONNECTION_TIMEOUT_SEC 30 /* Drop web connection after t.o. */ +#define WEB_CONNECTION_MAX_READ 8000 /* Max we'll read for TCP request */ +#define MAX_WEB_CONNECTIONS 10 /* max simultaneous web connects */ + + +static const char *urn_wfawlanconfig = + "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; +static const char *http_server_hdr = + "Server: unspecified, UPnP/1.0, unspecified\r\n"; +static const char *http_connection_close = + "Connection: close\r\n"; + +/* + * Incoming web connections are recorded in this struct. + * A web connection is a TCP connection to us, the server; + * it is called a "web connection" because we use http and serve + * data that looks like web pages. + * State information is need to track the connection until we figure + * out what they want and what we want to do about it. + */ +struct web_connection { + /* double linked list */ + struct web_connection *next; + struct web_connection *prev; + struct upnp_wps_device_sm *sm; /* parent */ + int sd; /* socket to read from */ + struct sockaddr_in cli_addr; + int sd_registered; /* nonzero if we must cancel registration */ + struct httpread *hread; /* state machine for reading socket */ + int n_rcvd_data; /* how much data read so far */ + int done; /* internal flag, set when we've finished */ +}; + + +/* + * XML parsing and formatting + * + * XML is a markup language based on unicode; usually (and in our case, + * always!) based on utf-8. utf-8 uses a variable number of bytes per + * character. utf-8 has the advantage that all non-ASCII unicode characters are + * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII + * characters are single ascii bytes, thus we can use typical text processing. + * + * (One other interesting thing about utf-8 is that it is possible to look at + * any random byte and determine if it is the first byte of a character as + * versus a continuation byte). + * + * The base syntax of XML uses a few ASCII punctionation characters; any + * characters that would appear in the payload data are rewritten using + * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). + * Five such escapes total (more can be defined but that does not apply to our + * case). Thus we can safely parse for angle brackets etc. + * + * XML describes tree structures of tagged data, with each element beginning + * with an opening tag <label> and ending with a closing tag </label> with + * matching label. (There is also a self-closing tag <label/> which is supposed + * to be equivalent to <label></label>, i.e., no payload, but we are unlikely + * to see it for our purpose). + * + * Actually the opening tags are a little more complicated because they can + * contain "attributes" after the label (delimited by ascii space or tab chars) + * of the form attribute_label="value" or attribute_label='value'; as it turns + * out we do not have to read any of these attributes, just ignore them. + * + * Labels are any sequence of chars other than space, tab, right angle bracket + * (and ?), but may have an inner structure of <namespace><colon><plain_label>. + * As it turns out, we can ignore the namespaces, in fact we can ignore the + * entire tree hierarchy, because the plain labels we are looking for will be + * unique (not in general, but for this application). We do however have to be + * careful to skip over the namespaces. + * + * In generating XML we have to be more careful, but that is easy because + * everything we do is pretty canned. The only real care to take is to escape + * any special chars in our payload. + */ + +/** + * xml_next_tag - Advance to next tag + * @in: Input + * @out: OUT: start of tag just after '<' + * @out_tagname: OUT: start of name of tag, skipping namespace + * @end: OUT: one after tag + * Returns: 0 on success, 1 on failure + * + * A tag has form: + * <left angle bracket><...><right angle bracket> + * Within the angle brackets, there is an optional leading forward slash (which + * makes the tag an ending tag), then an optional leading label (followed by + * colon) and then the tag name itself. + * + * Note that angle brackets present in the original data must have been encoded + * as < and > so they will not trouble us. + */ +static int xml_next_tag(char *in, char **out, char **out_tagname, + char **end) +{ + while (*in && *in != '<') + in++; + if (*in != '<') + return 1; + *out = ++in; + if (*in == '/') + in++; + *out_tagname = in; /* maybe */ + while (isalnum(*in) || *in == '-') + in++; + if (*in == ':') + *out_tagname = ++in; + while (*in && *in != '>') + in++; + if (*in != '>') + return 1; + *end = ++in; + return 0; +} + + +/* xml_data_encode -- format data for xml file, escaping special characters. + * + * Note that we assume we are using utf8 both as input and as output! + * In utf8, characters may be classed as follows: + * 0xxxxxxx(2) -- 1 byte ascii char + * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80 + * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here) + * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here) + * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here) + * 10xxxxxx(2) -- extension byte (6 payload bits per byte) + * Some values implied by the above are however illegal because they + * do not represent unicode chars or are not the shortest encoding. + * Actually, we can almost entirely ignore the above and just do + * text processing same as for ascii text. + * + * XML is written with arbitrary unicode characters, except that five + * characters have special meaning and so must be escaped where they + * appear in payload data... which we do here. + */ +static void xml_data_encode(struct wpabuf *buf, const char *data, int len) +{ + int i; + for (i = 0; i < len; i++) { + u8 c = ((u8 *) data)[i]; + if (c == '<') { + wpabuf_put_str(buf, "<"); + continue; + } + if (c == '>') { + wpabuf_put_str(buf, ">"); + continue; + } + if (c == '&') { + wpabuf_put_str(buf, "&"); + continue; + } + if (c == '\'') { + wpabuf_put_str(buf, "'"); + continue; + } + if (c == '"') { + wpabuf_put_str(buf, """); + continue; + } + /* + * We could try to represent control characters using the + * sequence: &#x; where x is replaced by a hex numeral, but not + * clear why we would do this. + */ + wpabuf_put_u8(buf, c); + } +} + + +/* xml_add_tagged_data -- format tagged data as a new xml line. + * + * tag must not have any special chars. + * data may have special chars, which are escaped. + */ +static void xml_add_tagged_data(struct wpabuf *buf, const char *tag, + const char *data) +{ + wpabuf_printf(buf, "<%s>", tag); + xml_data_encode(buf, data, os_strlen(data)); + wpabuf_printf(buf, "</%s>\n", tag); +} + + +/* A POST body looks something like (per upnp spec): + * <?xml version="1.0"?> + * <s:Envelope + * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" + * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> + * <s:Body> + * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> + * <argumentName>in arg value</argumentName> + * other in args and their values go here, if any + * </u:actionName> + * </s:Body> + * </s:Envelope> + * + * where : + * s: might be some other namespace name followed by colon + * u: might be some other namespace name followed by colon + * actionName will be replaced according to action requested + * schema following actionName will be WFA scheme instead + * argumentName will be actual argument name + * (in arg value) will be actual argument value + */ +static int +upnp_get_first_document_item(char *doc, const char *item, char **value) +{ + const char *match = item; + int match_len = os_strlen(item); + char *tag; + char *tagname; + char *end; + + *value = NULL; /* default, bad */ + + /* + * This is crude: ignore any possible tag name conflicts and go right + * to the first tag of this name. This should be ok for the limited + * domain of UPnP messages. + */ + for (;;) { + if (xml_next_tag(doc, &tag, &tagname, &end)) + return 1; + doc = end; + if (!os_strncasecmp(tagname, match, match_len) && + *tag != '/' && + (tagname[match_len] == '>' || + !isgraph(tagname[match_len]))) { + break; + } + } + end = doc; + while (*end && *end != '<') + end++; + *value = os_zalloc(1 + (end - doc)); + if (*value == NULL) + return 1; + os_memcpy(*value, doc, end - doc); + return 0; +} + + +/* + * "Files" that we serve via HTTP. The format of these files is given by + * WFA WPS specifications. Extra white space has been removed to save space. + */ + +static const char wps_scpd_xml[] = +"<?xml version=\"1.0\"?>\n" +"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n" +"<specVersion><major>1</major><minor>0</minor></specVersion>\n" +"<actionList>\n" +"<action>\n" +"<name>GetDeviceInfo</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewDeviceInfo</name>\n" +"<direction>out</direction>\n" +"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>PutMessage</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewInMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>InMessage</relatedStateVariable>\n" +"</argument>\n" +"<argument>\n" +"<name>NewOutMessage</name>\n" +"<direction>out</direction>\n" +"<relatedStateVariable>OutMessage</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>GetAPSettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"<argument>\n" +"<name>NewAPSettings</name>\n" +"<direction>out</direction>\n" +"<relatedStateVariable>APSettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>SetAPSettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>APSettings</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>APSettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>DelAPSettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewAPSettings</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>APSettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>GetSTASettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"<argument>\n" +"<name>NewSTASettings</name>\n" +"<direction>out</direction>\n" +"<relatedStateVariable>STASettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>SetSTASettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewSTASettings</name>\n" +"<direction>out</direction>\n" +"<relatedStateVariable>STASettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>DelSTASettings</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewSTASettings</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>STASettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>PutWLANResponse</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"<argument>\n" +"<name>NewWLANEventType</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>WLANEventType</relatedStateVariable>\n" +"</argument>\n" +"<argument>\n" +"<name>NewWLANEventMAC</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>SetSelectedRegistrar</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>RebootAP</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewAPSettings</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>APSettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>ResetAP</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>RebootSTA</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewSTASettings</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>APSettings</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"<action>\n" +"<name>ResetSTA</name>\n" +"<argumentList>\n" +"<argument>\n" +"<name>NewMessage</name>\n" +"<direction>in</direction>\n" +"<relatedStateVariable>Message</relatedStateVariable>\n" +"</argument>\n" +"</argumentList>\n" +"</action>\n" +"</actionList>\n" +"<serviceStateTable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>Message</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>InMessage</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>OutMessage</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>DeviceInfo</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>APSettings</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"yes\">\n" +"<name>APStatus</name>\n" +"<dataType>ui1</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>STASettings</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"yes\">\n" +"<name>STAStatus</name>\n" +"<dataType>ui1</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"yes\">\n" +"<name>WLANEvent</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>WLANEventType</name>\n" +"<dataType>ui1</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>WLANEventMAC</name>\n" +"<dataType>string</dataType>\n" +"</stateVariable>\n" +"<stateVariable sendEvents=\"no\">\n" +"<name>WLANResponse</name>\n" +"<dataType>bin.base64</dataType>\n" +"</stateVariable>\n" +"</serviceStateTable>\n" +"</scpd>\n" +; + + +static const char *wps_device_xml_prefix = + "<?xml version=\"1.0\"?>\n" + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n" + "<specVersion>\n" + "<major>1</major>\n" + "<minor>0</minor>\n" + "</specVersion>\n" + "<device>\n" + "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1" + "</deviceType>\n"; + +static const char *wps_device_xml_postfix = + "<serviceList>\n" + "<service>\n" + "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1" + "</serviceType>\n" + "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>" + "\n" + "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n" + "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n" + "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n" + "</service>\n" + "</serviceList>\n" + "</device>\n" + "</root>\n"; + + +/* format_wps_device_xml -- produce content of "file" wps_device.xml + * (UPNP_WPS_DEVICE_XML_FILE) + */ +static void format_wps_device_xml(struct upnp_wps_device_sm *sm, + struct wpabuf *buf) +{ + const char *s; + char uuid_string[80]; + + wpabuf_put_str(buf, wps_device_xml_prefix); + + /* + * Add required fields with default values if not configured. Add + * optional and recommended fields only if configured. + */ + s = sm->wps->friendly_name; + s = ((s && *s) ? s : "WPS Access Point"); + xml_add_tagged_data(buf, "friendlyName", s); + + s = sm->wps->dev.manufacturer; + s = ((s && *s) ? s : ""); + xml_add_tagged_data(buf, "manufacturer", s); + + if (sm->wps->manufacturer_url) + xml_add_tagged_data(buf, "manufacturerURL", + sm->wps->manufacturer_url); + + if (sm->wps->model_description) + xml_add_tagged_data(buf, "modelDescription", + sm->wps->model_description); + + s = sm->wps->dev.model_name; + s = ((s && *s) ? s : ""); + xml_add_tagged_data(buf, "modelName", s); + + if (sm->wps->dev.model_number) + xml_add_tagged_data(buf, "modelNumber", + sm->wps->dev.model_number); + + if (sm->wps->model_url) + xml_add_tagged_data(buf, "modelURL", sm->wps->model_url); + + if (sm->wps->dev.serial_number) + xml_add_tagged_data(buf, "serialNumber", + sm->wps->dev.serial_number); + + uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); + s = uuid_string; + /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data() + * easily... + */ + wpabuf_put_str(buf, "<UDN>uuid:"); + xml_data_encode(buf, s, os_strlen(s)); + wpabuf_put_str(buf, "</UDN>\n"); + + if (sm->wps->upc) + xml_add_tagged_data(buf, "UPC", sm->wps->upc); + + wpabuf_put_str(buf, wps_device_xml_postfix); +} + + +void web_connection_stop(struct web_connection *c) +{ + struct upnp_wps_device_sm *sm = c->sm; + + httpread_destroy(c->hread); + c->hread = NULL; + close(c->sd); + c->sd = -1; + if (c->next == c) { + sm->web_connections = NULL; + } else { + if (sm->web_connections == c) + sm->web_connections = c->next; + c->next->prev = c->prev; + c->prev->next = c->next; + } + os_free(c); + sm->n_web_connections--; +} + + +static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code) +{ + wpabuf_put_str(buf, "HTTP/1.1 "); + switch (code) { + case HTTP_OK: + wpabuf_put_str(buf, "200 OK\r\n"); + break; + case HTTP_BAD_REQUEST: + wpabuf_put_str(buf, "400 Bad request\r\n"); + break; + case HTTP_PRECONDITION_FAILED: + wpabuf_put_str(buf, "412 Precondition failed\r\n"); + break; + case HTTP_UNIMPLEMENTED: + wpabuf_put_str(buf, "501 Unimplemented\r\n"); + break; + case HTTP_INTERNAL_SERVER_ERROR: + default: + wpabuf_put_str(buf, "500 Internal server error\r\n"); + break; + } +} + + +static void http_put_date(struct wpabuf *buf) +{ + wpabuf_put_str(buf, "Date: "); + format_date(buf); + wpabuf_put_str(buf, "\r\n"); +} + + +static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) +{ + http_put_reply_code(buf, code); + wpabuf_put_str(buf, http_server_hdr); + wpabuf_put_str(buf, http_connection_close); + wpabuf_put_str(buf, "Content-Length: 0\r\n" + "\r\n"); +} + + +/* Given that we have received a header w/ GET, act upon it + * + * Format of GET (case-insensitive): + * + * First line must be: + * GET /<file> HTTP/1.1 + * Since we don't do anything fancy we just ignore other lines. + * + * Our response (if no error) which includes only required lines is: + * HTTP/1.1 200 OK + * Connection: close + * Content-Type: text/xml + * Date: <rfc1123-date> + * + * Header lines must end with \r\n + * Per RFC 2616, content-length: is not required but connection:close + * would appear to be required (given that we will be closing it!). + */ +static void web_connection_parse_get(struct web_connection *c, char *filename) +{ + struct upnp_wps_device_sm *sm = c->sm; + struct wpabuf *buf; /* output buffer, allocated */ + char *put_length_here; + char *body_start; + enum { + GET_DEVICE_XML_FILE, + GET_SCPD_XML_FILE + } req; + size_t extra_len = 0; + int body_length; + char len_buf[10]; + + /* + * It is not required that filenames be case insensitive but it is + * allowed and cannot hurt here. + */ + if (filename == NULL) + filename = "(null)"; /* just in case */ + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) { + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML"); + req = GET_DEVICE_XML_FILE; + extra_len = 3000; + if (sm->wps->friendly_name) + extra_len += os_strlen(sm->wps->friendly_name); + if (sm->wps->manufacturer_url) + extra_len += os_strlen(sm->wps->manufacturer_url); + if (sm->wps->model_description) + extra_len += os_strlen(sm->wps->model_description); + if (sm->wps->model_url) + extra_len += os_strlen(sm->wps->model_url); + if (sm->wps->upc) + extra_len += os_strlen(sm->wps->upc); + } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) { + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML"); + req = GET_SCPD_XML_FILE; + extra_len = os_strlen(wps_scpd_xml); + } else { + /* File not found */ + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s", + filename); + buf = wpabuf_alloc(200); + if (buf == NULL) + return; + wpabuf_put_str(buf, + "HTTP/1.1 404 Not Found\r\n" + "Connection: close\r\n"); + + http_put_date(buf); + + /* terminating empty line */ + wpabuf_put_str(buf, "\r\n"); + + goto send_buf; + } + + buf = wpabuf_alloc(1000 + extra_len); + if (buf == NULL) + return; + + wpabuf_put_str(buf, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n"); + wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n"); + wpabuf_put_str(buf, "Connection: close\r\n"); + wpabuf_put_str(buf, "Content-Length: "); + /* + * We will paste the length in later, leaving some extra whitespace. + * HTTP code is supposed to be tolerant of extra whitespace. + */ + put_length_here = wpabuf_put(buf, 0); + wpabuf_put_str(buf, " \r\n"); + + http_put_date(buf); + + /* terminating empty line */ + wpabuf_put_str(buf, "\r\n"); + + body_start = wpabuf_put(buf, 0); + + switch (req) { + case GET_DEVICE_XML_FILE: + format_wps_device_xml(sm, buf); + break; + case GET_SCPD_XML_FILE: + wpabuf_put_str(buf, wps_scpd_xml); + break; + } + + /* Now patch in the content length at the end */ + body_length = (char *) wpabuf_put(buf, 0) - body_start; + os_snprintf(len_buf, 10, "%d", body_length); + os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); + +send_buf: + send_wpabuf(c->sd, buf); + wpabuf_free(buf); +} + + +static struct wpabuf * web_get_item(char *data, const char *name, + enum http_reply_code *ret) +{ + char *msg; + struct wpabuf *buf; + unsigned char *decoded; + size_t len; + + if (upnp_get_first_document_item(data, name, &msg)) { + *ret = UPNP_ARG_VALUE_INVALID; + return NULL; + } + + decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len); + os_free(msg); + if (decoded == NULL) { + *ret = UPNP_OUT_OF_MEMORY; + return NULL; + } + + buf = wpabuf_alloc_ext_data(decoded, len); + if (buf == NULL) { + os_free(decoded); + *ret = UPNP_OUT_OF_MEMORY; + return NULL; + } + return buf; +} + + +static enum http_reply_code +web_process_get_device_info(struct upnp_wps_device_sm *sm, + struct wpabuf **reply, const char **replyname) +{ + static const char *name = "NewDeviceInfo"; + + wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); + if (sm->ctx->rx_req_get_device_info == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer); + if (*reply == NULL) { + wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); + return HTTP_INTERNAL_SERVER_ERROR; + } + *replyname = name; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_put_message(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + static const char *name = "NewOutMessage"; + enum http_reply_code ret; + + /* + * PutMessage is used by external UPnP-based Registrar to perform WPS + * operation with the access point itself; as compared with + * PutWLANResponse which is for proxying. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage"); + if (sm->ctx->rx_req_put_message == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + msg = web_get_item(data, "NewInMessage", &ret); + if (msg == NULL) + return ret; + *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg); + wpabuf_free(msg); + if (*reply == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + *replyname = name; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + static const char *name = "NewAPSettings"; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings"); + if (sm->ctx->rx_req_get_ap_settings == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg); + wpabuf_free(msg); + if (*reply == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + *replyname = name; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings"); + msg = web_get_item(data, "NewAPSettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_set_ap_settings || + sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings"); + msg = web_get_item(data, "NewAPSettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_del_ap_settings || + sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + static const char *name = "NewSTASettings"; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings"); + if (sm->ctx->rx_req_get_sta_settings == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg); + wpabuf_free(msg); + if (*reply == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + *replyname = name; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings"); + msg = web_get_item(data, "NewSTASettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_set_sta_settings || + sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings"); + msg = web_get_item(data, "NewSTASettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_del_sta_settings || + sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + u8 macaddr[ETH_ALEN]; + int ev_type; + int type; + char *val; + + /* + * External UPnP-based Registrar is passing us a message to be proxied + * over to a Wi-Fi -based client of ours. + */ + + wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse"); + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) { + wpabuf_free(msg); + return UPNP_ARG_VALUE_INVALID; + } + ev_type = atol(val); + os_free(val); + val = NULL; + if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) || + hwaddr_aton(val, macaddr)) { + wpabuf_free(msg); + os_free(val); + return UPNP_ARG_VALUE_INVALID; + } + os_free(val); + if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) { + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) < 0 || + attr.msg_type == NULL) + type = -1; + else + type = *attr.msg_type; + wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type); + } else + type = -1; + if (!sm->ctx->rx_req_put_wlan_response || + sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg, + type)) { + wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->" + "rx_req_put_wlan_response"); + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, + const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_set_selected_registrar || + sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP"); + msg = web_get_item(data, "NewAPSettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_reboot_ap || + sm->ctx->rx_req_reboot_ap(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP"); + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_reset_ap || + sm->ctx->rx_req_reset_ap(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA"); + msg = web_get_item(data, "NewSTASettings", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_reboot_sta || + sm->ctx->rx_req_reboot_sta(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static enum http_reply_code +web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data, + struct wpabuf **reply, const char **replyname) +{ + struct wpabuf *msg; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA"); + msg = web_get_item(data, "NewMessage", &ret); + if (msg == NULL) + return ret; + if (!sm->ctx->rx_req_reset_sta || + sm->ctx->rx_req_reset_sta(sm->priv, msg)) { + wpabuf_free(msg); + return HTTP_INTERNAL_SERVER_ERROR; + } + wpabuf_free(msg); + *replyname = NULL; + *reply = NULL; + return HTTP_OK; +} + + +static const char *soap_prefix = + "<?xml version=\"1.0\"?>\n" + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" + "<s:Body>\n"; +static const char *soap_postfix = + "</s:Body>\n</s:Envelope>\n"; + +static const char *soap_error_prefix = + "<s:Fault>\n" + "<faultcode>s:Client</faultcode>\n" + "<faultstring>UPnPError</faultstring>\n" + "<detail>\n" + "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n"; +static const char *soap_error_postfix = + "<errorDescription>Error</errorDescription>\n" + "</UPnPError>\n" + "</detail>\n" + "</s:Fault>\n"; + +static void web_connection_send_reply(struct web_connection *c, + enum http_reply_code ret, + const char *action, int action_len, + const struct wpabuf *reply, + const char *replyname) +{ + struct wpabuf *buf; + char *replydata; + char *put_length_here = NULL; + char *body_start = NULL; + + if (reply) { + size_t len; + replydata = (char *) base64_encode(wpabuf_head(reply), + wpabuf_len(reply), &len); + } else + replydata = NULL; + + /* Parameters of the response: + * action(action_len) -- action we are responding to + * replyname -- a name we need for the reply + * replydata -- NULL or null-terminated string + */ + buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) + + (action_len > 0 ? action_len * 2 : 0)); + if (buf == NULL) { + wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to " + "POST"); + wpabuf_free(buf); + os_free(replydata); + return; + } + + /* + * Assuming we will be successful, put in the output header first. + * Note: we do not keep connections alive (and httpread does + * not support it)... therefore we must have Connection: close. + */ + if (ret == HTTP_OK) { + wpabuf_put_str(buf, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml; " + "charset=\"utf-8\"\r\n"); + } else { + wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret); + } + wpabuf_put_str(buf, http_connection_close); + + wpabuf_put_str(buf, "Content-Length: "); + /* + * We will paste the length in later, leaving some extra whitespace. + * HTTP code is supposed to be tolerant of extra whitespace. + */ + put_length_here = wpabuf_put(buf, 0); + wpabuf_put_str(buf, " \r\n"); + + http_put_date(buf); + + /* terminating empty line */ + wpabuf_put_str(buf, "\r\n"); + + body_start = wpabuf_put(buf, 0); + + if (ret == HTTP_OK) { + wpabuf_put_str(buf, soap_prefix); + wpabuf_put_str(buf, "<u:"); + wpabuf_put_data(buf, action, action_len); + wpabuf_put_str(buf, "Response xmlns:u=\""); + wpabuf_put_str(buf, urn_wfawlanconfig); + wpabuf_put_str(buf, "\">\n"); + if (replydata && replyname) { + /* TODO: might possibly need to escape part of reply + * data? ... + * probably not, unlikely to have ampersand(&) or left + * angle bracket (<) in it... + */ + wpabuf_printf(buf, "<%s>", replyname); + wpabuf_put_str(buf, replydata); + wpabuf_printf(buf, "</%s>\n", replyname); + } + wpabuf_put_str(buf, "</u:"); + wpabuf_put_data(buf, action, action_len); + wpabuf_put_str(buf, "Response>\n"); + wpabuf_put_str(buf, soap_postfix); + } else { + /* Error case */ + wpabuf_put_str(buf, soap_prefix); + wpabuf_put_str(buf, soap_error_prefix); + wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret); + wpabuf_put_str(buf, soap_error_postfix); + wpabuf_put_str(buf, soap_postfix); + } + os_free(replydata); + + /* Now patch in the content length at the end */ + if (body_start && put_length_here) { + int body_length = (char *) wpabuf_put(buf, 0) - body_start; + char len_buf[10]; + os_snprintf(len_buf, sizeof(len_buf), "%d", body_length); + os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); + } + + send_wpabuf(c->sd, buf); + wpabuf_free(buf); +} + + +static const char * web_get_action(struct web_connection *c, + const char *filename, size_t *action_len) +{ + const char *match; + int match_len; + char *b; + char *action; + + *action_len = 0; + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", + filename); + return NULL; + } + /* The SOAPAction line of the header tells us what we want to do */ + b = httpread_hdr_line_get(c->hread, "SOAPAction:"); + if (b == NULL) + return NULL; + if (*b == '"') + b++; + else + return NULL; + match = urn_wfawlanconfig; + match_len = os_strlen(urn_wfawlanconfig) - 1; + if (os_strncasecmp(b, match, match_len)) + return NULL; + b += match_len; + /* skip over version */ + while (isgraph(*b) && *b != '#') + b++; + if (*b != '#') + return NULL; + b++; + /* Following the sharp(#) should be the action and a double quote */ + action = b; + while (isgraph(*b) && *b != '"') + b++; + if (*b != '"') + return NULL; + *action_len = b - action; + return action; +} + + +/* Given that we have received a header w/ POST, act upon it + * + * Format of POST (case-insensitive): + * + * First line must be: + * POST /<file> HTTP/1.1 + * Since we don't do anything fancy we just ignore other lines. + * + * Our response (if no error) which includes only required lines is: + * HTTP/1.1 200 OK + * Connection: close + * Content-Type: text/xml + * Date: <rfc1123-date> + * + * Header lines must end with \r\n + * Per RFC 2616, content-length: is not required but connection:close + * would appear to be required (given that we will be closing it!). + */ +static void web_connection_parse_post(struct web_connection *c, + const char *filename) +{ + enum http_reply_code ret; + struct upnp_wps_device_sm *sm = c->sm; + char *data = httpread_data_get(c->hread); /* body of http msg */ + const char *action; + size_t action_len; + const char *replyname = NULL; /* argument name for the reply */ + struct wpabuf *reply = NULL; /* data for the reply */ + + ret = UPNP_INVALID_ACTION; + action = web_get_action(c, filename, &action_len); + if (action == NULL) + goto bad; + + /* + * There are quite a few possible actions. Although we appear to + * support them all here, not all of them are necessarily supported by + * callbacks at higher levels. + */ + if (!os_strncasecmp("GetDeviceInfo", action, action_len)) + ret = web_process_get_device_info(sm, &reply, &replyname); + else if (!os_strncasecmp("PutMessage", action, action_len)) + ret = web_process_put_message(sm, data, &reply, &replyname); + else if (!os_strncasecmp("GetAPSettings", action, action_len)) + ret = web_process_get_ap_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("SetAPSettings", action, action_len)) + ret = web_process_set_ap_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("DelAPSettings", action, action_len)) + ret = web_process_del_ap_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("GetSTASettings", action, action_len)) + ret = web_process_get_sta_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("SetSTASettings", action, action_len)) + ret = web_process_set_sta_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("DelSTASettings", action, action_len)) + ret = web_process_del_sta_settings(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("PutWLANResponse", action, action_len)) + ret = web_process_put_wlan_response(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len)) + ret = web_process_set_selected_registrar(sm, data, &reply, + &replyname); + else if (!os_strncasecmp("RebootAP", action, action_len)) + ret = web_process_reboot_ap(sm, data, &reply, &replyname); + else if (!os_strncasecmp("ResetAP", action, action_len)) + ret = web_process_reset_ap(sm, data, &reply, &replyname); + else if (!os_strncasecmp("RebootSTA", action, action_len)) + ret = web_process_reboot_sta(sm, data, &reply, &replyname); + else if (!os_strncasecmp("ResetSTA", action, action_len)) + ret = web_process_reset_sta(sm, data, &reply, &replyname); + else + wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type"); + +bad: + if (ret != HTTP_OK) + wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret); + web_connection_send_reply(c, ret, action, action_len, reply, + replyname); + wpabuf_free(reply); +} + + +/* Given that we have received a header w/ SUBSCRIBE, act upon it + * + * Format of SUBSCRIBE (case-insensitive): + * + * First line must be: + * SUBSCRIBE /wps_event HTTP/1.1 + * + * Our response (if no error) which includes only required lines is: + * HTTP/1.1 200 OK + * Server: xx, UPnP/1.0, xx + * SID: uuid:xxxxxxxxx + * Timeout: Second-<n> + * Content-Length: 0 + * Date: xxxx + * + * Header lines must end with \r\n + * Per RFC 2616, content-length: is not required but connection:close + * would appear to be required (given that we will be closing it!). + */ +static void web_connection_parse_subscribe(struct web_connection *c, + const char *filename) +{ + struct upnp_wps_device_sm *sm = c->sm; + struct wpabuf *buf; + char *b; + char *hdr = httpread_hdr_get(c->hread); + char *h; + char *match; + int match_len; + char *end; + int len; + int got_nt = 0; + u8 uuid[UUID_LEN]; + int got_uuid = 0; + char *callback_urls = NULL; + struct subscription *s = NULL; + enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return; + + /* Parse/validate headers */ + h = hdr; + /* First line: SUBSCRIBE /wps_event HTTP/1.1 + * has already been parsed. + */ + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) { + ret = HTTP_PRECONDITION_FAILED; + goto error; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event"); + end = os_strchr(h, '\n'); + + for (; end != NULL; h = end + 1) { + /* Option line by option line */ + h = end + 1; + end = os_strchr(h, '\n'); + if (end == NULL) + break; /* no unterminated lines allowed */ + + /* NT assures that it is our type of subscription; + * not used for a renewl. + **/ + match = "NT:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + match = "upnp:event"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) != 0) { + ret = HTTP_BAD_REQUEST; + goto error; + } + got_nt = 1; + continue; + } + /* HOST should refer to us */ +#if 0 + match = "HOST:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + ..... + } +#endif + /* CALLBACK gives one or more URLs for NOTIFYs + * to be sent as a result of the subscription. + * Each URL is enclosed in angle brackets. + */ + match = "CALLBACK:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + len = end - h; + os_free(callback_urls); + callback_urls = os_malloc(len + 1); + if (callback_urls == NULL) { + ret = HTTP_INTERNAL_SERVER_ERROR; + goto error; + } + os_memcpy(callback_urls, h, len); + callback_urls[len] = 0; + continue; + } + /* SID is only for renewal */ + match = "SID:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + match = "uuid:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) != 0) { + ret = HTTP_BAD_REQUEST; + goto error; + } + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + if (uuid_str2bin(h, uuid)) { + ret = HTTP_BAD_REQUEST; + goto error; + } + got_uuid = 1; + continue; + } + /* TIMEOUT is requested timeout, but apparently we can + * just ignore this. + */ + } + + if (got_uuid) { + /* renewal */ + if (callback_urls) { + ret = HTTP_BAD_REQUEST; + goto error; + } + s = subscription_renew(sm, uuid); + if (s == NULL) { + ret = HTTP_PRECONDITION_FAILED; + goto error; + } + } else if (callback_urls) { + if (!got_nt) { + ret = HTTP_PRECONDITION_FAILED; + goto error; + } + s = subscription_start(sm, callback_urls); + if (s == NULL) { + ret = HTTP_INTERNAL_SERVER_ERROR; + goto error; + } + callback_urls = NULL; /* is now owned by subscription */ + } else { + ret = HTTP_PRECONDITION_FAILED; + goto error; + } + + /* success */ + http_put_reply_code(buf, HTTP_OK); + wpabuf_put_str(buf, http_server_hdr); + wpabuf_put_str(buf, http_connection_close); + wpabuf_put_str(buf, "Content-Length: 0\r\n"); + wpabuf_put_str(buf, "SID: uuid:"); + /* subscription id */ + b = wpabuf_put(buf, 0); + uuid_bin2str(s->uuid, b, 80); + wpabuf_put(buf, os_strlen(b)); + wpabuf_put_str(buf, "\r\n"); + wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC); + http_put_date(buf); + /* And empty line to terminate header: */ + wpabuf_put_str(buf, "\r\n"); + + send_wpabuf(c->sd, buf); + wpabuf_free(buf); + os_free(callback_urls); + return; + +error: + /* Per UPnP spec: + * Errors + * Incompatible headers + * 400 Bad Request. If SID header and one of NT or CALLBACK headers + * are present, the publisher must respond with HTTP error + * 400 Bad Request. + * Missing or invalid CALLBACK + * 412 Precondition Failed. If CALLBACK header is missing or does not + * contain a valid HTTP URL, the publisher must respond with HTTP + * error 412 Precondition Failed. + * Invalid NT + * 412 Precondition Failed. If NT header does not equal upnp:event, + * the publisher must respond with HTTP error 412 Precondition + * Failed. + * [For resubscription, use 412 if unknown uuid]. + * Unable to accept subscription + * 5xx. If a publisher is not able to accept a subscription (such as + * due to insufficient resources), it must respond with a + * HTTP 500-series error code. + * 599 Too many subscriptions (not a standard HTTP error) + */ + http_put_empty(buf, ret); + send_wpabuf(c->sd, buf); + wpabuf_free(buf); +} + + +/* Given that we have received a header w/ UNSUBSCRIBE, act upon it + * + * Format of UNSUBSCRIBE (case-insensitive): + * + * First line must be: + * UNSUBSCRIBE /wps_event HTTP/1.1 + * + * Our response (if no error) which includes only required lines is: + * HTTP/1.1 200 OK + * Content-Length: 0 + * + * Header lines must end with \r\n + * Per RFC 2616, content-length: is not required but connection:close + * would appear to be required (given that we will be closing it!). + */ +static void web_connection_parse_unsubscribe(struct web_connection *c, + const char *filename) +{ + struct upnp_wps_device_sm *sm = c->sm; + struct wpabuf *buf; + char *hdr = httpread_hdr_get(c->hread); + char *h; + char *match; + int match_len; + char *end; + u8 uuid[UUID_LEN]; + int got_uuid = 0; + struct subscription *s = NULL; + enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; + + /* Parse/validate headers */ + h = hdr; + /* First line: UNSUBSCRIBE /wps_event HTTP/1.1 + * has already been parsed. + */ + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) { + ret = HTTP_PRECONDITION_FAILED; + goto send_msg; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event"); + end = os_strchr(h, '\n'); + + for (; end != NULL; h = end + 1) { + /* Option line by option line */ + h = end + 1; + end = os_strchr(h, '\n'); + if (end == NULL) + break; /* no unterminated lines allowed */ + + /* HOST should refer to us */ +#if 0 + match = "HOST:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + ..... + } +#endif + /* SID is only for renewal */ + match = "SID:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + match = "uuid:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) != 0) { + ret = HTTP_BAD_REQUEST; + goto send_msg; + } + h += match_len; + while (*h == ' ' || *h == '\t') + h++; + if (uuid_str2bin(h, uuid)) { + ret = HTTP_BAD_REQUEST; + goto send_msg; + } + got_uuid = 1; + continue; + } + } + + if (got_uuid) { + s = subscription_find(sm, uuid); + if (s) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", + s, + (s && s->addr_list && + s->addr_list->domain_and_port) ? + s->addr_list->domain_and_port : "-null-"); + subscription_unlink(s); + subscription_destroy(s); + } + } else { + wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not " + "found)"); + ret = HTTP_PRECONDITION_FAILED; + goto send_msg; + } + + ret = HTTP_OK; + +send_msg: + buf = wpabuf_alloc(200); + if (buf == NULL) + return; + http_put_empty(buf, ret); + send_wpabuf(c->sd, buf); + wpabuf_free(buf); +} + + +/* Send error in response to unknown requests */ +static void web_connection_unimplemented(struct web_connection *c) +{ + struct wpabuf *buf; + buf = wpabuf_alloc(200); + if (buf == NULL) + return; + http_put_empty(buf, HTTP_UNIMPLEMENTED); + send_wpabuf(c->sd, buf); + wpabuf_free(buf); +} + + + +/* Called when we have gotten an apparently valid http request. + */ +static void web_connection_check_data(struct web_connection *c) +{ + struct httpread *hread = c->hread; + enum httpread_hdr_type htype = httpread_hdr_type_get(hread); + /* char *data = httpread_data_get(hread); */ + char *filename = httpread_uri_get(hread); + + c->done = 1; + if (!filename) { + wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI"); + return; + } + /* Trim leading slashes from filename */ + while (*filename == '/') + filename++; + + wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d", + htype, inet_ntoa(c->cli_addr.sin_addr), + htons(c->cli_addr.sin_port)); + + switch (htype) { + case HTTPREAD_HDR_TYPE_GET: + web_connection_parse_get(c, filename); + break; + case HTTPREAD_HDR_TYPE_POST: + web_connection_parse_post(c, filename); + break; + case HTTPREAD_HDR_TYPE_SUBSCRIBE: + web_connection_parse_subscribe(c, filename); + break; + case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: + web_connection_parse_unsubscribe(c, filename); + break; + /* We are not required to support M-POST; just plain + * POST is supposed to work, so we only support that. + * If for some reason we need to support M-POST, it is + * mostly the same as POST, with small differences. + */ + default: + /* Send 501 for anything else */ + web_connection_unimplemented(c); + break; + } +} + + + +/* called back when we have gotten request */ +static void web_connection_got_file_handler(struct httpread *handle, + void *cookie, + enum httpread_event en) +{ + struct web_connection *c = cookie; + + if (en == HTTPREAD_EVENT_FILE_READY) + web_connection_check_data(c); + web_connection_stop(c); +} + + +/* web_connection_start - Start web connection + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @sd: Socket descriptor + * @addr: Client address + * + * The socket descriptor sd is handed over for ownership by the WPS UPnP + * state machine. + */ +static void web_connection_start(struct upnp_wps_device_sm *sm, + int sd, struct sockaddr_in *addr) +{ + struct web_connection *c = NULL; + + /* if too many connections, bail */ + if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) { + close(sd); + return; + } + + c = os_zalloc(sizeof(*c)); + if (c == NULL) + return; + os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr)); + c->sm = sm; + c->sd = sd; +#if 0 + /* + * Setting non-blocking should not be necessary for read, and can mess + * up sending where blocking might be better. + */ + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + break; +#endif + c->hread = httpread_create(c->sd, web_connection_got_file_handler, + c /* cookie */, + WEB_CONNECTION_MAX_READ, + WEB_CONNECTION_TIMEOUT_SEC); + if (c->hread == NULL) + goto fail; + if (sm->web_connections) { + c->next = sm->web_connections; + c->prev = c->next->prev; + c->prev->next = c; + c->next->prev = c; + } else { + sm->web_connections = c->next = c->prev = c; + } + sm->n_web_connections++; + return; + +fail: + if (c) + web_connection_stop(c); +} + + +/* + * Listening for web connections + * We have a single TCP listening port, and hand off connections as we get + * them. + */ + +void web_listener_stop(struct upnp_wps_device_sm *sm) +{ + if (sm->web_sd_registered) { + sm->web_sd_registered = 0; + eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ); + } + if (sm->web_sd >= 0) + close(sm->web_sd); + sm->web_sd = -1; +} + + +static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + struct upnp_wps_device_sm *sm = sock_ctx; + int new_sd; + + /* Create state for new connection */ + /* Remember so we can cancel if need be */ + new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len); + if (new_sd < 0) { + wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept " + "errno=%d (%s) web_sd=%d", + errno, strerror(errno), sm->web_sd); + return; + } + web_connection_start(sm, new_sd, &addr); +} + + +int web_listener_start(struct upnp_wps_device_sm *sm) +{ + struct sockaddr_in addr; + int port; + + sm->web_sd = socket(AF_INET, SOCK_STREAM, 0); + if (sm->web_sd < 0) + goto fail; + if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) + goto fail; + port = 49152; /* first non-reserved port */ + for (;;) { + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = sm->ip_addr; + addr.sin_port = htons(port); + if (bind(sm->web_sd, (struct sockaddr *) &addr, + sizeof(addr)) == 0) + break; + if (errno == EADDRINUSE) { + /* search for unused port */ + if (++port == 65535) + goto fail; + continue; + } + goto fail; + } + if (listen(sm->web_sd, 10 /* max backlog */) != 0) + goto fail; + if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) + goto fail; + if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ, + web_listener_handler, NULL, sm)) + goto fail; + sm->web_sd_registered = 1; + sm->web_port = port; + + return 0; + +fail: + /* Error */ + web_listener_stop(sm); + return -1; +} diff --git a/wpa_supplicant/.gitignore b/wpa_supplicant/.gitignore new file mode 100644 index 0000000000000..e7e034c7fe801 --- /dev/null +++ b/wpa_supplicant/.gitignore @@ -0,0 +1,8 @@ +*.d +.config +eapol_test +preauth_test +wpa_cli +wpa_passphrase +wpa_supplicant +wpa_priv diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog new file mode 100644 index 0000000000000..8f5b6fbe1a0a5 --- /dev/null +++ b/wpa_supplicant/ChangeLog @@ -0,0 +1,1173 @@ +ChangeLog for wpa_supplicant + +2009-02-15 - v0.6.8 + * increased wpa_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G<seconds>) + * fixed scan buffer processing with WEXT to handle up to 65535 + byte result buffer (previously, limited to 32768 bytes) + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (wpa_supplicant can now be configured to act as a WPS Enrollee to + enroll credentials for a network using PIN and PBC methods; in + addition, wpa_supplicant can act as a wireless WPS Registrar to + configure an AP); WPS support can be enabled by adding CONFIG_WPS=y + into .config and setting the runtime configuration variables in + wpa_supplicant.conf (see WPS section in the example configuration + file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to + manage WPS negotiation; see README-WPS for more details + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * fixed PEAPv0 Cryptobinding interoperability issue with Windows Server + 2008 NPS; optional cryptobinding is now enabled (again) by default + * fixed PSK editing in wpa_gui + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * added a Windows installer that includes WinPcap and all the needed + DLLs; in addition, it set up the registry automatically so that user + will only need start wpa_gui to get prompted to start the wpasvc + servide and add a new interface if needed through wpa_gui dialog + * updated management frame protection to use IEEE 802.11w/D7.0 + +2008-11-23 - v0.6.6 + * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA + (can be used to simulate test SIM/USIM card with a known private key; + enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config + and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration) + * added a new network configuration option, wpa_ptk_rekey, that can be + used to enforce frequent PTK rekeying, e.g., to mitigate some attacks + against TKIP deficiencies + * added an optional mitigation mechanism for certain attacks against + TKIP by delaying Michael MIC error reports by a random amount of time + between 0 and 60 seconds; this can be enabled with a build option + CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config + * fixed EAP-AKA to use RES Length field in AT_RES as length in bits, + not bytes + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * updated userspace MLME instructions to match with the current Linux + mac80211 implementation; please also note that this can only be used + with driver_nl80211.c (the old code from driver_wext.c was removed) + * added support (Linux only) for RoboSwitch chipsets (often found in + consumer grade routers); driver interface 'roboswitch' + * fixed canceling of PMKSA caching when using drivers that generate + RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know + about + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * updated management frame protection to use IEEE 802.11w/D6.0 + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * fixed FT (IEEE 802.11r) authentication after a failed association to + use correct FTIE + * added support for configuring Phase 2 (inner/tunneled) authentication + method with wpa_gui-qt4 + +2008-08-10 - v0.6.4 + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for using TNC with EAP-FAST + * added driver_ps3 for the PS3 Linux wireless driver + * added support for optional cryptobinding with PEAPv0 + * fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to + allow fallback to full handshake if server rejects PAC-Opaque + * added fragmentation support for EAP-TNC + * added support for parsing PKCS #8 formatted private keys into the + internal TLS implementation (both PKCS #1 RSA key and PKCS #8 + encapsulated RSA key can now be used) + * added option of using faster, but larger, routines in the internal + LibTomMath (for internal TLS implementation) to speed up DH and RSA + calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y) + * fixed race condition between disassociation event and group key + handshake to avoid getting stuck in incorrect state [Bug 261] + * fixed opportunistic key caching (proactive_key_caching) + +2008-02-22 - v0.6.3 + * removed 'nai' and 'eappsk' network configuration variables that were + previously used for configuring user identity and key for EAP-PSK, + EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the + replacement for 'nai' (if old configuration used a separate + 'identity' value, that would now be configured as + 'anonymous_identity'). 'password' field is now used as the + replacement for 'eappsk' (it can also be set using hexstring to + present random binary data) + * removed '-w' command line parameter (wait for interface to be added, + if needed); cleaner way of handling this functionality is to use an + external mechanism (e.g., hotplug scripts) that start wpa_supplicant + when an interface is added + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for + indicating when new scan results become available + * added new ctrl_iface command, BSS, to allow scan results to be + fetched without hitting the message size limits (this command + can be used to iterate through the scan results one BSS at the time) + * fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION + attributes in EAP-SIM Start/Response when using fast reauthentication + * fixed EAPOL not to end up in infinite loop when processing dynamic + WEP keys with IEEE 802.1X + * fixed problems in getting NDIS events from WMI on Windows 2000 + +2008-01-01 - v0.6.2 + * added support for Makefile builds to include debug-log-to-a-file + functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line) + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect wpa_supplicant usage, but many of the APIs + changed and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (phase1="result_ind=1") + * added driver_wext workaround for race condition between scanning and + association with drivers that take very long time to scan all + channels (e.g., madwifi with dual-band cards); wpa_supplicant is now + using a longer hardcoded timeout for the scan if the driver supports + notifications for scan completion (SIOCGIWSCAN event); this helps, + e.g., in cases where wpa_supplicant and madwifi driver ended up in + loop where the driver did not even try to associate + * stop EAPOL timer tick when no timers are in use in order to reduce + power consumption (no need to wake up the process once per second) + [Bug 237] + * added support for privilege separation (run only minimal part of + wpa_supplicant functionality as root and rest as unprivileged, + non-root process); see 'Privilege separation' in README for details; + this is disabled by default and can be enabled with CONFIG_PRIVSEP=y + in .config + * changed scan results data structure to include all information + elements to make it easier to support new IEs; old get_scan_result() + driver_ops is still supported for backwards compatibility (results + are converted internally to the new format), but all drivers should + start using the new get_scan_results2() to make them more likely to + work with new features + * Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4 + application, i.e., it does not require Qt3Support anymore; Windows + binary of wpa_gui.exe is now from this directory and only requires + QtCore4.dll and QtGui4.dll libraries + * updated Windows binary build to use Qt 4.3.3 and made Qt DLLs + available as a separate package to make wpa_gui installation easier: + http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added support for configuring password as NtPasswordHash + (16-byte MD4 hash of password) in hash:<32 hex digits> format + * added support for fallback from abbreviated TLS handshake to + full handshake when using EAP-FAST (e.g., due to an expired + PAC-Opaque) + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-07.txt) + * added support for drivers that take care of RSN 4-way handshake + internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and + WPA_ALG_PMK in set_key) + * added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in + .config); this version supports only ap_scan=2 mode and allow the + driver to take care of the 4-way handshake + * fixed a buffer overflow in parsing TSF from scan results when using + driver_wext.c with a driver that includes the TSF (e.g., iwl4965) + [Bug 232] + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + * fixed an integer overflow issue in the ASN.1 parser used by the + (experimental) internal TLS implementation to avoid a potential + buffer read overflow + * fixed a race condition with -W option (wait for a control interface + monitor before starting) that could have caused the first messages to + be lost + * added support for processing TNCC-TNCS-Messages to report + recommendation (allow/none/isolate) when using TNC [Bug 243] + +2007-05-28 - v0.6.0 + * added network configuration parameter 'frequency' for setting + initial channel for IBSS (adhoc) networks + * added experimental IEEE 802.11r/D6.0 support + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PAX key derivation + * fixed EAP-PSK bit ordering of the Flags field + * fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in + tunnelled identity request (previously, the identifier from the outer + method was used, not the tunnelled identifier which could be + different) + * added support for fragmentation of outer TLS packets during Phase 2 + of EAP-PEAP/TTLS/FAST + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * added support for EAP-FAST authentication with inner methods that + generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported + for PAC provisioning) + * added support for authenticated EAP-FAST provisioning + * added support for configuring maximum number of EAP-FAST PACs to + store in a PAC list (fast_max_pac_list_len=<max> in phase1 string) + * added support for storing EAP-FAST PACs in binary format + (fast_pac_format=binary in phase1 string) + * fixed dbus ctrl_iface to validate message interface before + dispatching to avoid a possible segfault [Bug 190] + * fixed PeerKey key derivation to use the correct PRF label + * updated Windows binary build to link against OpenSSL 0.9.8d and + added support for EAP-FAST + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * fixed EAP-AKA Notification processing to allow Notification to be + processed after AKA Challenge response has been sent + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * fixed EAP-TTLS implementation not to crash on use of freed memory + if TLS library initialization fails + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS changes + needed to run two methods in sequence (IF-T) and the IF-IMC and + IF-TNCCS interfaces from TNCC) + +2006-11-24 - v0.5.6 + * added experimental, integrated TLSv1 client implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + (total size of this internal TLS/crypto code is bit under 50 kB on + x86 and the crypto code is shared by rest of the supplicant so some + of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * fixed Windows named pipes ctrl_iface to not stop listening for + commands if client program opens a named pipe and closes it + immediately without sending a command + * fixed USIM PIN status determination for the case that PIN is not + needed (this allows EAP-AKA to be used with USIM cards that do not + use PIN) + * added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to + be used with cards that do not support file selection based on + partial AID + * added support for matching the subjectAltName of the authentication + server certificate against multiple name components (e.g., + altsubject_match="DNS:server.example.com;DNS:server2.example.com") + * fixed EAP-SIM/AKA key derivation for re-authentication case (only + affects IEEE 802.1X with dynamic WEP keys) + * changed ctrl_iface network configuration 'get' operations to not + return password/key material; if these fields are requested, "*" + will be returned if the password/key is set, but the value of the + parameter is not exposed + +2006-08-27 - v0.5.5 + * added support for building Windows version with UNICODE defined + (wide-char functions) + * driver_ndis: fixed static WEP configuration to avoid race condition + issues with some NDIS drivers between association and setting WEP + keys + * driver_ndis: added validation for IELength value in scan results to + avoid crashes when using buggy NDIS drivers [Bug 165] + * fixed Release|Win32 target in the Visual Studio project files + (previously, only Debug|Win32 target was set properly) + * changed control interface API call wpa_ctrl_pending() to allow it to + return -1 on error (e.g., connection lost); control interface clients + will need to make sure that they verify that the value is indeed >0 + when determining whether there are pending messages + * added an alternative control interface backend for Windows targets: + Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default + control interface mechanism for Windows builds (previously, UDP to + localhost was used) + * changed ctrl_interface configuration for UNIX domain sockets: + - deprecated ctrl_interface_group variable (it may be removed in + future versions) + - allow both directory and group be configured with ctrl_interface + in following format: DIR=/var/run/wpa_supplicant GROUP=wheel + - ctrl_interface=/var/run/wpa_supplicant is still supported for the + case when group is not changed + * added support for controlling more than one interface per process in + Windows version + * added a workaround for a case where the AP is using unknown address + (e.g., MAC address of the wired interface) as the source address for + EAPOL-Key frames; previously, that source address was used as the + destination for EAPOL-Key frames and in key derivation; now, BSSID is + used even if the source address does not match with it + (this resolves an interoperability issue with Thomson SpeedTouch 580) + * added a workaround for UDP-based control interface (which was used in + Windows builds before this release) to prevent packets with forged + addresses from being accepted as local control requests + * removed ndis_events.cpp and possibility of using external + ndis_events.exe; C version (ndis_events.c) is fully functional and + there is no desire to maintain two separate versions of this + implementation + * ndis_events: Changed NDIS event notification design to use WMI to + learn the adapter description through Win32_PnPEntity class; this + should fix some cases where the adapter name was not recognized + correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500 + USB) [Bug 113] + * fixed selection of the first network in ap_scan=2 mode; previously, + wpa_supplicant could get stuck in SCANNING state when only the first + network for enabled (e.g., after 'wpa_cli select_network 0') + * winsvc: added support for configuring ctrl_interface parameters in + registry (ctrl_interface string value in + HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is + required to enable control interface (previously, this was hardcoded + to be enabled) + * allow wpa_gui subdirectory to be built with both Qt3 and Qt4 + * converted wpa_gui-qt4 subdirectory to use Qt4 specific project format + +2006-06-20 - v0.5.4 + * fixed build with CONFIG_STAKEY=y [Bug 143] + * added support for doing MLME (IEEE 802.11 management frame + processing) in wpa_supplicant when using Devicescape IEEE 802.11 + stack (wireless-dev.git tree) + * added a new network block configuration option, fragment_size, to + configure the maximum EAP fragment size + * driver_ndis: Disable WZC automatically for the selected interface to + avoid conflicts with two programs trying to control the radio; WZC + will be re-enabled (if it was enabled originally) when wpa_supplicant + is terminated + * added an experimental TLSv1 client implementation + (CONFIG_TLS=internal) that can be used instead of an external TLS + library, e.g., to reduce total size requirement on systems that do + not include any TLS library by default (this is not yet complete; + basic functionality is there, but certificate validation is not yet + included) + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * fixed WPA PSK update through ctrl_iface for the case where the old + PSK was derived from an ASCII passphrase and the new PSK is set as + a raw PSK (hex string) + * added new configuration option for identifying which network block + was used (id_str in wpa_supplicant.conf; included on + WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental + variable in wpa_cli action scripts; in addition WPA_ID variable is + set to the current unique identifier that wpa_supplicant assigned + automatically for the network and that can be used with + GET_NETWORK/SET_NETWORK ctrl_iface commands) + * wpa_cli action script is now called only when the connect/disconnect + status changes or when associating with a different network + * fixed configuration parser not to remove CCMP from group cipher list + if WPA-None (adhoc) is used (pairwise=NONE in that case) + * fixed integrated NDIS events processing not to hang the process due + to a missed change in eloop_win.c API in v0.5.3 [Bug 155] + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * added Microsoft Visual Studio 2005 solution and project files for + build wpa_supplicant for Windows (see vs2005 subdirectory) + * eloop_win: fixed unregistration of Windows events + * l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet + at the end of RSN pre-authentication and added unregistration of + a Windows event to avoid getting eloop_win stuck with an invalid + handle + * driver_ndis: added support for selecting AP based on BSSID + * added new environmental variable for wpa_cli action scripts: + WPA_CTRL_DIR is the current control interface directory + * driver_ndis: added support for using NDISUIO instead of WinPcap for + OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new + l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build + wpa_supplicant without requiring WinPcap; note that using NDISUIO + requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows + only one application to open the device + * changed NDIS driver naming to only include device GUID, e.g., + {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap + specific \Device\NPF_ prefix before the GUID; the prefix is still + allowed for backwards compatibility, but it is not required anymore + when specifying the interface + * driver_ndis: re-initialize driver interface is the adapter is removed + and re-inserted [Bug 159] + * driver_madwifi: fixed TKIP and CCMP sequence number configuration on + big endian hosts [Bug 146] + +2006-04-27 - v0.5.3 + * fixed EAP-GTC response to include correct user identity when run as + phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2) + * driver_ndis: Fixed encryption mode configuration for unencrypted + networks (some NDIS drivers ignored this, but others, e.g., Broadcom, + refused to associate with open networks) [Bug 106] + * driver_ndis: use BSSID OID polling to detect when IBSS network is + formed even when ndis_events code is included since some NDIS drivers + do not generate media connect events in IBSS mode + * config_winreg: allow global ctrl_interface parameter to be configured + in Windows registry + * config_winreg: added support for saving configuration data into + Windows registry + * added support for controlling network device operational state + (dormant/up) for Linux 2.6.17 to improve DHCP processing (see + http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client + that can use this information) + * driver_wext: added support for WE-21 change to SSID configuration + * driver_wext: fixed privacy configuration for static WEP keys mode + [Bug 140] + * added an optional driver_ops callback for MLME-SETPROTECTION.request + primitive + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * added support for dynamically loading EAP methods (.so files) instead + of requiring them to be statically linked in; this is disabled by + default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information + on how to use this) + +2006-03-19 - v0.5.2 + * do not try to use USIM APDUs when initializing PC/SC for SIM card + access for a network that has not enabled EAP-AKA + * fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in + v0.5.1 due to the new support for expanded EAP types) + * added support for generating EAP Expanded Nak + * try to fetch scan results once before requesting new scan when + starting up in ap_scan=1 mode (this can speed up initial association + a lot with, e.g., madwifi-ng driver) + * added support for receiving EAPOL frames from a Linux bridge + interface (-bbr0 on command line) + * fixed EAPOL re-authentication for sessions that used PMKSA caching + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed PMKSA cache deinitialization not to use freed memory when + removing PMKSA entries + * fixed a memory leak in EAP-TTLS re-authentication + * reject WPA/WPA2 message 3/4 if it does not include any valid + WPA/RSN IE + * driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg + if the driver does not support SIOCSIWAUTH + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (driver_param=test_dir=/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + * added AP_SCAN command into ctrl_iface so that ap_scan configuration + option can be changed if needed + * wpa_cli/wpa_gui: skip non-socket files in control directory when + using UNIX domain sockets; this avoids selecting an incorrect + interface (e.g., a PID file could be in this directory, even though + use of this directory for something else than socket files is not + recommended) + * fixed TLS library deinitialization after RSN pre-authentication not + to disable TLS library for normal authentication + * driver_wext: Remove null-termination from SSID length if the driver + used it; some Linux drivers do this and they were causing problems in + wpa_supplicant not finding matching configuration block. This change + would break a case where the SSID actually ends in '\0', but that is + not likely to happen in real use. + * fixed PMKSA cache processing not to trigger deauthentication if the + current PMKSA cache entry is replaced with a valid new entry + * fixed PC/SC initialization for ap_scan != 1 modes (this fixes + EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or + ap_scan=2) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to + decrypt AT_ENCR_DATA attributes correctly + * fixed EAP-AKA to allow resynchronization within the same session + * made code closer to ANSI C89 standard to make it easier to port to + other C libraries and compilers + * started moving operating system or C library specific functions into + wrapper functions defined in os.h and implemented in os_*.c to make + code more portable + * wpa_supplicant can now be built with Microsoft Visual C++ + (e.g., with the freely available Toolkit 2003 version or Visual + C++ 2005 Express Edition and Platform SDK); see nmake.mak for an + example makefile for nmake + * added support for using Windows registry for command line parameters + (CONFIG_MAIN=main_winsvc) and configuration data + (CONFIG_BACKEND=winreg); see win_example.reg for an example registry + contents; this version can be run both as a Windows service and as a + normal application; 'wpasvc.exe app' to start as applicant, + 'wpasvc.exe reg <full path to wpasvc.exe>' to register a service, + 'net start wpasvc' to start the service, 'wpasvc.exe unreg' to + unregister a service + * made it possible to link ndis_events.exe functionality into + wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED + * added better support for multiple control interface backends + (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported + * fixed PC/SC code to use correct length for GSM AUTH command buffer + and to not use pioRecvPci with SCardTransmit() calls; these were not + causing visible problems with pcsc-lite, but Windows Winscard.dll + refused the previously used parameters; this fixes EAP-SIM and + EAP-AKA authentication using SIM/USIM card under Windows + * added new event loop implementation for Windows using + WaitForMultipleObject() instead of select() in order to allow waiting + for non-socket objects; this can be selected with + CONFIG_ELOOP=eloop_win in .config + * added support for selecting l2_packet implementation in .config + (CONFIG_L2_PACKET; following options are available now: linux, pcap, + winpcap, freebsd, none) + * added new l2_packet implementation for WinPcap + (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to + reduce latency in EAPOL receive processing from about 100 ms to about + 3 ms + * added support for EAP-FAST key derivation using other ciphers than + RC4-128-SHA for authentication and AES128-SHA for provisioning + * added support for configuring CA certificate as DER file and as a + configuration blob + * fixed private key configuration as configuration blob and added + support for using PKCS#12 as a blob + * tls_gnutls: added support for using PKCS#12 files; added support for + session resumption + * added support for loading trusted CA certificates from Windows + certificate store: ca_cert="cert_store://<name>", where <name> is + likely CA (Intermediate CA certificates) or ROOT (root certificates) + * added C version of ndis_events.cpp and made it possible to build this + with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more + easily on cross-compilation builds + * added wpasvc.exe into Windows binary release; this is an alternative + version of wpa_supplicant.exe with configuration backend using + Windows registry and with the entry point designed to run as a + Windows service + * integrated ndis_events.exe functionality into wpa_supplicant.exe and + wpasvc.exe and removed this additional tool from the Windows binary + release since it is not needed anymore + * load winscard.dll functions dynamically when building with MinGW + since MinGW does not yet include winscard library + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap + and WinPcap to receive frames sent to PAE group address + * disable EAP state machine when IEEE 802.1X authentication is not used + in order to get rid of bogus "EAP failed" messages + * fixed OpenSSL error reporting to go through all pending errors to + avoid confusing reports of old errors being reported at later point + during handshake + * fixed configuration file updating to not write empty variables + (e.g., proto or key_mgmt) that the file parser would not accept + * fixed ADD_NETWORK ctrl_iface command to use the same default values + for variables as empty network definitions read from config file + would get + * fixed EAP state machine to not discard EAP-Failure messages in many + cases (e.g., during TLS handshake) + * fixed a infinite loop in private key reading if the configured file + cannot be parsed successfully + * driver_madwifi: added support for madwifi-ng + * wpa_gui: do not display password/PSK field contents + * wpa_gui: added CA certificate configuration + * driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID + * driver_ndis: include Beacon IEs in AssocInfo in order to notice if + the new AP is using different WPA/RSN IE + * use longer timeout for IEEE 802.11 association to avoid problems with + drivers that may take more than five second to associate + +2005-10-27 - v0.4.6 + * allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in + RSN IE, but WPA IE would match with wpa_supplicant configuration + * added support for named configuration blobs in order to avoid having + to use file system for external files (e.g., certificates); + variables can be set to "blob://<blob name>" instead of file path to + use a named blob; supported fields: pac_file, client_cert, + private_key + * fixed RSN pre-authentication (it was broken in the clean up of WPA + state machine interface in v0.4.5) + * driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make + sure the driver configures broadcast decryption correctly + * added ca_path (and ca_path2) configuration variables that can be used + to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the + system-wide trusted CA list + * added support for starting wpa_supplicant without a configuration + file (-C argument must be used to set ctrl_interface parameter for + this case; in addition, -p argument can be used to provide + driver_param; these new arguments can also be used with a + configuration to override the values from the configuration) + * added global control interface that can be optionally used for adding + and removing network interfaces dynamically (-g command line argument + for both wpa_supplicant and wpa_cli) without having to restart + wpa_supplicant process + * wpa_gui: + - try to save configuration whenever something is modified + - added WEP key configuration + - added possibility to edit the current network configuration + * driver_ndis: fixed driver polling not to increase frequency on each + received EAPOL frame due to incorrectly cancelled timeout + * added simple configuration file examples (in examples subdirectory) + * fixed driver_wext.c to filter wireless events based on ifindex to + avoid interfaces receiving events from other interfaces + * delay sending initial EAPOL-Start couple of seconds to speed up + authentication for the most common case of Authenticator starting + EAP authentication immediately after association + +2005-09-25 - v0.4.5 + * added a workaround for clearing keys with ndiswrapper to allow + roaming from WPA enabled AP to plaintext one + * added docbook documentation (doc/docbook) that can be used to + generate, e.g., man pages + * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for + PF_PACKET in order to prepare for network devices that do not use + Ethernet headers (e.g., network stack with native IEEE 802.11 frames) + * use receipt of EAPOL-Key frame as a lower layer success indication + for EAP state machine to allow recovery from dropped EAP-Success + frame + * cleaned up internal EAPOL frame processing by not including link + layer (Ethernet) header during WPA and EAPOL/EAP processing; this + header is added only when transmitted the frame; this makes it easier + to use wpa_supplicant on link layers that use different header than + Ethernet + * updated EAP-PSK to use draft 9 by default since this can now be + tested with hostapd; removed support for draft 3, including + server_nai configuration option from network blocks + * driver_wired: add PAE address to the multicast address list in order + to be able to receive EAPOL frames with drivers that do not include + these multicast addresses by default + * driver_wext: add support for WE-19 + * added support for multiple configuration backends (CONFIG_BACKEND + option); currently, only 'file' is supported (i.e., the format used + in wpa_supplicant.conf) + * added support for updating configuration ('wpa_cli save_config'); + this is disabled by default and can be enabled with global + update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli + and wpa_gui to store the configuration changes in a permanent store + * added GET_NETWORK ctrl_iface command + (e.g., 'wpa_cli get_network 0 ssid') + +2005-08-21 - v0.4.4 + * replaced OpenSSL patch for EAP-FAST support + (openssl-tls-extensions.patch) with a more generic and correct + patch (the new patch is not compatible with the previous one, so the + OpenSSL library will need to be patched with the new patch in order + to be able to build wpa_supplicant with EAP-FAST support) + * added support for using Windows certificate store (through CryptoAPI) + for client certificate and private key operations (EAP-TLS) + (see wpa_supplicant.conf for more information on how to configure + this with private_key) + * ported wpa_gui to Windows + * added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be + built with the open source version of the Qt4 for Windows + * allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used + with drivers that do not support WPA + * ndis_events: fixed Windows 2000 support + * added support for enabling/disabling networks from the list of all + configured networks ('wpa_cli enable_network <network id>' and + 'wpa_cli disable_network <network id>') + * added support for adding and removing network from the current + configuration ('wpa_cli add_network' and 'wpa_cli remove_network + <network id>'); added networks are disabled by default and they can + be enabled with enable_network command once the configuration is done + for the new network; note: configuration file is not yet updated, so + these new networks are lost when wpa_supplicant is restarted + * added support for setting network configuration parameters through + the control interface, for example: + wpa_cli set_network 0 ssid "\"my network\"" + * fixed parsing of strings that include both " and # within double + quoted area (e.g., "start"#end") + * added EAP workaround for PEAP session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + (this was allowed for PEAPv1 before, but now it is also allowed for + PEAPv0 since at least one RADIUS authentication server seems to be + doing this for PEAPv0, too) + * wpa_gui: added preliminary support for adding new networks to the + wpa_supplicant configuration (double click on the scan results to + open network configuration) + +2005-06-26 - v0.4.3 + * removed interface for external EAPOL/EAP supplicant (e.g., + Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required + anymore and is unlikely to be used by anyone + * driver_ndis: fixed WinPcap 3.0 support + * fixed build with CONFIG_DNET_PCAP=y on Linux + * l2_packet: moved different implementations into separate files + (l2_packet_*.c) + +2005-06-12 - v0.4.2 + * driver_ipw: updated driver structures to match with ipw2200-1.0.4 + (note: ipw2100-1.1.0 is likely to require an update to work with + this) + * added support for using ap_scan=2 mode with multiple network blocks; + wpa_supplicant will go through the networks one by one until the + driver reports a successful association; this uses the same order for + networks as scan_ssid=1 scans, i.e., the priority field is ignored + and the network block order in the file is used instead + * fixed a potential issue in RSN pre-authentication ending up using + freed memory if pre-authentication times out + * added support for matching alternative subject name extensions of the + authentication server certificate; new configuration variables + altsubject_match and altsubject_match2 + * driver_ndis: added support for IEEE 802.1X authentication with wired + NDIS drivers + * added support for querying private key password (EAP-TLS) through the + control interface (wpa_cli/wpa_gui) if one is not included in the + configuration file + * driver_broadcom: fixed couple of memory leaks in scan result + processing + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * added support for using password with EAP-PAX (as an alternative to + entering key with eappsk); SHA-1 hash of the password will be used as + the key in this case + * added support for arbitrary driver interface parameters through the + configuration file with a new driver_param field; this adds a new + driver_ops function set_param() + * added possibility to override l2_packet module with driver interface + API (new send_eapol handler); this can be used to implement driver + specific TX/RX functions for EAPOL frames + * fixed ctrl_interface_group processing for the case where gid is + entered as a number, not group name + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL + packets to be encrypted; this was apparently broken by the changed + ioctl order in v0.4.0 + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * added support for EAP-MSCHAPv2 password retries within the same EAP + authentication session + * added support for password changes with EAP-MSCHAPv2 (used when the + password has expired) + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys + were used + * fixed a possible double free in EAP-TTLS fast-reauthentication when + identity or password is entered through control interface + * display EAP Notification messages to user through control interface + with "CTRL-EVENT-EAP-NOTIFICATION" prefix + * added GUI version of wpa_cli, wpa_gui; this is not build + automatically with 'make'; use 'make wpa_gui' to build (this requires + Qt development tools) + * added 'disconnect' command to control interface for setting + wpa_supplicant in state where it will not associate before + 'reassociate' command has been used + * added support for selecting a network from the list of all configured + networks ('wpa_cli select_network <network id>'; this disabled all + other networks; to re-enable, 'wpa_cli select_network any') + * added support for getting scan results through control interface + * added EAP workaround for PEAPv1 session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be + used to reduce the size of the wpa_supplicant considerably if + debugging code is not needed + * fixed EAPOL-Key validation to drop packets with invalid Key Data + Length; such frames could have crashed wpa_supplicant due to buffer + overflow + * added support for wired authentication (IEEE 802.1X on wired + Ethernet); driver interface 'wired' + * obsoleted set_wpa() handler in the driver interface API (it can be + replaced by moving enable/disable functionality into init()/deinit()) + (calls to set_wpa() are still present for backwards compatibility, + but they may be removed in the future) + * driver_madwifi: fixed association in plaintext mode + * modified the EAP workaround that accepts EAP-Success with incorrect + Identifier to be even less strict about verification in order to + interoperate with some authentication servers + * added support for sending TLS alerts + * added support for 'any' SSID wildcard; if ssid is not configured or + is set to an empty string, any SSID will be accepted for non-WPA AP + * added support for asking PIN (for SIM) from frontends (e.g., + wpa_cli); if a PIN is needed, but not included in the configuration + file, a control interface request is sent and EAP processing is + delayed until the PIN is available + * added support for using external devices (e.g., a smartcard) for + private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config); + new wpa_supplicant.conf variables: + - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path + - network: engine, engine_id, key_id + * added experimental support for EAP-PAX + * added monitor mode for wpa_cli (-a<path to a program to run>) that + allows external commands (e.g., shell scripts) to be run based on + wpa_supplicant events, e.g., when authentication has been completed + and data connection is ready; other related wpa_cli arguments: + -B (run in background), -P (write PID file); wpa_supplicant has a new + command line argument (-W) that can be used to make it wait until a + control interface command is received in order to avoid missing + events + * added support for opportunistic WPA2 PMKSA key caching (disabled by + default, can be enabled with proactive_key_caching=1) + * fixed RSN IE in 4-Way Handshake message 2/4 for the case where + Authenticator rejects PMKSA caching attempt and the driver is not + using assoc_info events + * added -P<pid file> argument for wpa_supplicant to write the current + process id into a file + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + * added new phase1 option parameter, include_tls_length=1, to force + wpa_supplicant to add TLS Message Length field to all TLS messages + even if the packet is not fragmented; this may be needed with some + authentication servers + * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when + using drivers that take care of AP selection (e.g., when using + ap_scan=2) + * fixed reprocessing of pending request after ctrl_iface requests for + identity/password/otp + * fixed ctrl_iface requests for identity/password/otp in Phase 2 of + EAP-PEAP and EAP-TTLS + * all drivers using driver_wext: set interface up and select Managed + mode when starting wpa_supplicant; set interface down when exiting + * renamed driver_ipw2100.c to driver_ipw.c since it now supports both + ipw2100 and ipw2200; please note that this also changed the + configuration variable in .config to CONFIG_DRIVER_IPW + +2005-01-24 - v0.3.6 + * fixed a busy loop introduced in v0.3.5 for scan result processing + when no matching AP is found + +2005-01-23 - v0.3.5 + * added a workaround for an interoperability issue with a Cisco AP + when using WPA2-PSK + * fixed non-WPA IEEE 802.1X to use the same authentication timeout as + WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow + retransmission of dropped frames) + * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version + (e.g., segfault when processing EAPOL-Key frames) + * fixed EAP workaround and fast reauthentication configuration for + RSN pre-authentication; previously these were disabled and + pre-authentication would fail if the used authentication server + requires EAP workarounds + * added support for blacklisting APs that fail or timeout + authentication in ap_scan=1 mode so that all APs are tried in cases + where the ones with strongest signal level are failing authentication + * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS + authentication attempt + * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded + in the previous authentication (previously, only Phase 1 success was + verified) + +2005-01-09 - v0.3.4 + * added preliminary support for IBSS (ad-hoc) mode configuration + (mode=1 in network block); this included a new key_mgmt mode + WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no + key management; see wpa_supplicant.conf for more details and an + example on how to configure this (note: this is currently implemented + only for driver_hostapd.c, but the changes should be trivial to add + in associate() handler for other drivers, too (assuming the driver + supports WPA-None) + * added preliminary port for native Windows (i.e., no cygwin) using + mingw + +2005-01-02 - v0.3.3 + * added optional support for GNU Readline and History Libraries for + wpa_cli (CONFIG_READLINE) + * cleaned up EAP state machine <-> method interface and number of + small problems with error case processing not terminating on + EAP-Failure but waiting for timeout + * added couple of workarounds for interoperability issues with a + Cisco AP when using WPA2 + * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt); + Note: This requires a patch for openssl to add support for TLS + extensions and number of workarounds for operations without + certificates. Proof of concept type of experimental patch is + included in openssl-tls-extensions.patch. + +2004-12-19 - v0.3.2 + * fixed private key loading for cases where passphrase is not set + * fixed Windows/cygwin L2 packet handler freeing; previous version + could cause a segfault when RSN pre-authentication was completed + * added support for PMKSA caching with drivers that generate RSN IEs + (e.g., NDIS); currently, this is only implemented in driver_ndis.c, + but similar code can be easily added to driver_ndiswrapper.c once + ndiswrapper gets full support for RSN PMKSA caching + * improved recovery from PMKID mismatches by requesting full EAP + authentication in case of failed PMKSA caching attempt + * driver_ndis: added support for NDIS NdisMIncidateStatus() events + (this requires that ndis_events is ran while wpa_supplicant is + running) + * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys + * added support for driver interfaces to replace the interface name + based on driver/OS specific mapping, e.g., in case of driver_ndis, + this allows the beginning of the adapter description to be used as + the interface name + * added support for CR+LF (Windows-style) line ends in configuration + file + * driver_ndis: enable radio before starting scanning, disable radio + when exiting + * modified association event handler to set portEnabled = FALSE before + clearing port Valid in order to reset EAP state machine and avoid + problems with new authentication getting ignored because of state + machines ending up in AUTHENTICATED/SUCCESS state based on old + information + * added support for driver events to add PMKID candidates in order to + allow drivers to give priority to most likely roaming candidates + * driver_hostap: moved PrivacyInvoked configuration to associate() + function so that this will not be set for plaintext connections + * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver + interface can distinguish plaintext and IEEE 802.1X (no WPA) + authentication + * fixed static WEP key configuration to use broadcast/default type for + all keys (previously, the default TX key was configured as pairwise/ + unicast key) + * driver_ndis: added legacy WPA capability detection for non-WPA2 + drivers + * added support for setting static WEP keys for IEEE 802.1X without + dynamic WEP keying (eapol_flags=0) + +2004-12-12 - v0.3.1 + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + * fixed compilation with CONFIG_PCSC=y + * added new ap_scan mode, ap_scan=2, for drivers that take care of + association, but need to be configured with security policy and SSID, + e.g., ndiswrapper and NDIS driver; this mode should allow such + drivers to work with hidden SSIDs and optimized roaming; when + ap_scan=2 is used, only the first network block in the configuration + file is used and this configuration should have explicit security + policy (i.e., only one option in the lists) for key_mgmt, pairwise, + group, proto variables + * added experimental port of wpa_supplicant for Windows + - driver_ndis.c driver interface (NDIS OIDs) + - currently, this requires cygwin and WinPcap + - small utility, win_if_list, can be used to get interface name + * control interface can now be removed at build time; add + CONFIG_CTRL_IFACE=y to .config to maintain old functionality + * optional Xsupplicant interface can now be removed at build time; + (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back) + * added auth_alg to driver interface associate() parameters to make it + easier for drivers to configure authentication algorithm as part of + the association + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * driver_broadcom: added new driver interface for Broadcom wl.o driver + (a generic driver for Broadcom IEEE 802.11a/g cards) + * wpa_cli: fixed parsing of -p <path> command line argument + * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS + ACK, not tunneled EAP-Success (of which only the first byte was + actually send due to a bug in previous code); this seems to + interoperate with most RADIUS servers that implements PEAPv1 + * PEAPv1: added support for terminating PEAP authentication on tunneled + EAP-Success message; this can be configured by adding + peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf + (some RADIUS servers require this whereas others require a tunneled + reply + * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to + the old label for key derivation; previously, the default was 1, + but it looks like most existing PEAPv1 implementations use the old + label which is thus more suitable default option + * added support for EAP-PSK (draft-bersani-eap-psk-03.txt) + * fixed parsing of wep_tx_keyidx + * added support for configuring list of allowed Phase 2 EAP types + (for both EAP-PEAP and EAP-TTLS) instead of only one type + * added support for configuring IEEE 802.11 authentication algorithm + (auth_alg; mainly for using Shared Key authentication with static + WEP keys) + * added support for EAP-AKA (with UMTS SIM) + * fixed couple of errors in PCSC handling that could have caused + random-looking errors for EAP-SIM + * added support for EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS + session resumption) + * added support for EAP-SIM with two challanges + (phase1="sim_min_num_chal=3" can be used to require three challenges) + * added support for configuring DH/DSA parameters for an ephemeral DH + key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters + dh_file and dh_file2 (phase 2); this adds support for using DSA keys + and optional DH key exchange to achieve forward secracy with RSA keys + * added support for matching subject of the authentication server + certificate with a substring when using EAP-TLS/PEAP/TTLS; new + configuration variables subject_match and subject_match2 + * changed SSID configuration in driver_wext.c (used by many driver + interfaces) to use ssid_len+1 as the length for SSID since some Linux + drivers expect this + * fixed couple of unaligned reads in scan result parsing to fix WPA + connection on some platforms (e.g., ARM) + * added driver interface for Intel ipw2100 driver + * added support for LEAP with WPA + * added support for larger scan results report (old limit was 4 kB of + data, i.e., about 35 or so APs) when using Linux wireless extensions + v17 or newer + * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start + only if there is a PMKSA cache entry for the current AP + * fixed error handling for case where reading of scan results fails: + must schedule a new scan or wpa_supplicant will remain waiting + forever + * changed debug output to remove shared password/key material by + default; all key information can be included with -K command line + argument to match the previous behavior + * added support for timestamping debug log messages (disabled by + default, can be enabled with -t command line argument) + * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104 + if keys are not configured to be used; this fixes IEEE 802.1X mode + with drivers that use this information to configure whether Privacy + bit can be in Beacon frames (e.g., ndiswrapper) + * avoid clearing driver keys if no keys have been configured since last + key clear request; this seems to improve reliability of group key + handshake for ndiswrapper & NDIS driver which seems to be suffering + of some kind of timing issue when the keys are cleared again after + association + * changed driver interface API: + - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which + version is being used (now, this is set to 2; previously, it was + not defined) + - pass pointer to private data structure to all calls + - the new API is not backwards compatible; all in-tree driver + interfaces has been converted to the new API + * added support for controlling multiple interfaces (radios) per + wpa_supplicant process; each interface needs to be listed on the + command line (-c, -i, -D arguments) with -N as a separator + (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi) + * added a workaround for EAP servers that incorrectly use same Id for + sequential EAP packets + * changed libpcap/libdnet configuration to use .config variable, + CONFIG_DNET_PCAP, instead of requiring Makefile modification + * improved downgrade attack detection in IE verification of msg 3/4: + verify both WPA and RSN IEs, if present, not only the selected one; + reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or + Probe Response frame, and RSN is enabled in wpa_supplicant + configuration + * fixed WPA msg 3/4 processing to allow Key Data field contain other + IEs than just one WPA IE + * added support for FreeBSD and driver interface for the BSD net80211 + layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the + required kernel mods have not yet been committed + * made EAP workarounds configurable; enabled by default, can be + disabled with network block option eap_workaround=0 + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * resolved couple of interoperability issues with EAP-PEAPv1 and + Phase 2 (inner EAP) fragment reassembly + * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the + AP is using non-zero key index for the unicast key and key index zero + for the broadcast key + * driver_hostap: fixed IEEE 802.1X WEP key updates and + re-authentication by allowing unencrypted EAPOL frames when not using + WPA + * added a new driver interface, 'wext', which uses only standard, + driver independent functionality in Linux wireless extensions; + currently, this can be used only for non-WPA IEEE 802.1X mode, but + eventually, this is to be extended to support full WPA/WPA2 once + Linux wireless extensions get support for this + * added support for mode in which the driver is responsible for AP + scanning and selection; this is disabled by default and can be + enabled with global ap_scan=0 variable in wpa_supplicant.conf; + this mode can be used, e.g., with generic 'wext' driver interface to + use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver + supporting wireless extensions. + * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g., + operation with an AP that does not include SSID in the Beacon frames) + * added support for new EAP authentication methods: + EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP + * added support for asking one-time-passwords from frontends (e.g., + wpa_cli); this 'otp' command works otherwise like 'password' command, + but the password is used only once and the frontend will be asked for + a new password whenever a request from authenticator requires a + password; this can be used with both EAP-OTP and EAP-GTC + * changed wpa_cli to automatically re-establish connection so that it + does not need to be re-started when wpa_supplicant is terminated and + started again + * improved user data (identity/password/otp) requests through + frontends: process pending EAPOL packets after getting new + information so that full authentication does not need to be + restarted; in addition, send pending requests again whenever a new + frontend is attached + * changed control frontends to use a new directory for socket files to + make it easier for wpa_cli to automatically select between interfaces + and to provide access control for the control interface; + wpa_supplicant.conf: ctrl_interface is now a path + (/var/run/wpa_supplicant is the recommended path) and + ctrl_interface_group can be used to select which group gets access to + the control interface; + wpa_cli: by default, try to connect to the first interface available + in /var/run/wpa_supplicant; this path can be overriden with -p option + and an interface can be selected with -i option (i.e., in most common + cases, wpa_cli does not need to get any arguments) + * added support for LEAP + * added driver interface for Linux ndiswrapper + * added priority option for network blocks in the configuration file; + this allows networks to be grouped based on priority (the scan + results are searched for matches with network blocks in this order) + +2004-06-20 - v0.2.3 + * sort scan results to improve AP selection + * fixed control interface socket removal for some error cases + * improved scan requesting and authentication timeout + * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and + TLS processing + * PEAP version can now be forced with phase1="peapver=<ver>" + (mostly for testing; by default, the highest version supported by + both the Supplicant and Authentication Server is selected + automatically) + * added support for madwifi driver (Atheros ar521x) + * added a workaround for cases where AP sets Install Tx/Rx bit for + WPA Group Key messages when pairwise keys are used (without this, + the Group Key would be used for Tx and the AP would drop frames + from the station) + * added GSM SIM/USIM interface for GSM authentication algorithm for + EAP-SIM; this requires pcsc-lite + * added support for ATMEL AT76C5XXx driver + * fixed IEEE 802.1X WEP key derivation in the case where Authenticator + does not include key data in the EAPOL-Key frame (i.e., part of + EAP keying material is used as data encryption key) + * added support for using plaintext and static WEP networks + (key_mgmt=NONE) + +2004-05-31 - v0.2.2 + * added support for new EAP authentication methods: + EAP-TTLS/EAP-MD5-Challenge + EAP-TTLS/EAP-GTC + EAP-TTLS/EAP-MSCHAPv2 + EAP-TTLS/EAP-TLS + EAP-TTLS/MSCHAPv2 + EAP-TTLS/MSCHAP + EAP-TTLS/PAP + EAP-TTLS/CHAP + EAP-PEAP/TLS + EAP-PEAP/GTC + EAP-PEAP/MD5-Challenge + EAP-GTC + EAP-SIM (not yet complete; needs GSM/SIM authentication interface) + * added support for anonymous identity (to be used when identity is + sent in plaintext; real identity will be used within TLS protected + tunnel (e.g., with EAP-TTLS) + * added event messages from wpa_supplicant to frontends, e.g., wpa_cli + * added support for requesting identity and password information using + control interface; in other words, the password for EAP-PEAP or + EAP-TTLS does not need to be included in the configuration file since + a frontand (e.g., wpa_cli) can ask it from the user + * improved RSN pre-authentication to use a candidate list and process + all candidates from each scan; not only one per scan + * fixed RSN IE and WPA IE capabilities field parsing + * ignore Tx bit in GTK IE when Pairwise keys are used + * avoid making new scan requests during IEEE 802.1X negotiation + * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant + with TLS support (this replaces the included implementation with + library code to save about 8 kB since the library code is needed + anyway for TLS) + * fixed WPA-PSK only mode when compiled without IEEE 802.1X support + (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config) + +2004-05-06 - v0.2.1 + * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1) + Supplicant + - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1] + - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf] + - EAP-MD5 (cannot be used with WPA-RADIUS) + [draft-ietf-eap-rfc2284bis-09.txt] + - EAP-TLS [RFC 2716] + - EAP-MSCHAPv2 (currently used only with EAP-PEAP) + - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt] + [draft-kamath-pppext-eap-mschapv2-00.txt] + (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by + default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey) + - new configuration file options: eap, identity, password, ca_cert, + client_cert, privatekey, private_key_passwd + - Xsupplicant is not required anymore, but it can be used by + disabling the internal IEEE 802.1X Supplicant with -e command line + option + - this code is not included in the default build; Makefile need to + be edited for this (uncomment lines for selected functionality) + - EAP-TLS and EAP-PEAP require openssl libraries + * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..) + * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys + (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X + EAPOL-Key frames instead of WPA key handshakes) + * added support for IEEE 802.11i/RSN (WPA2) + - improved PTK Key Handshake + - PMKSA caching, pre-authentication + * fixed wpa_supplicant to ignore possible extra data after WPA + EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using + TPTK' error from message 3 of 4-Way Handshake in case the AP + includes extra data after the EAPOL-Key) + * added interface for external programs (frontends) to control + wpa_supplicant + - CLI example (wpa_cli) with interactive mode and command line + mode + - replaced SIGUSR1 status/statistics with the new control interface + * made some feature compile time configurable + - .config file for make + - driver interfaces (hostap, hermes, ..) + - EAPOL/EAP functions + +2004-02-15 - v0.2.0 + * Initial version of wpa_supplicant diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile new file mode 100644 index 0000000000000..0a50ceef0b97e --- /dev/null +++ b/wpa_supplicant/Makefile @@ -0,0 +1,1246 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +export LIBDIR ?= /usr/local/lib/ +export BINDIR ?= /usr/local/sbin/ + +CFLAGS += -I../src +CFLAGS += -I../src/crypto +CFLAGS += -I../src/utils +CFLAGS += -I../src/common +CFLAGS += -I../src/rsn_supp + +ALL=wpa_supplicant wpa_passphrase wpa_cli + +all: verify_config $(ALL) dynamic_eap_methods + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -e .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + echo CONFIG_WIRELESS_EXTENSION=y >> .config + +install: all + mkdir -p $(DESTDIR)$(BINDIR) + for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done + $(MAKE) -C ../src install + +OBJS = config.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS += ../src/crypto/md5.o +OBJS += ../src/crypto/rc4.o +OBJS += ../src/crypto/md4.o +OBJS += ../src/crypto/sha1.o +OBJS += ../src/crypto/des.o +OBJS_p = wpa_passphrase.o +OBJS_p += ../src/utils/common.o +OBJS_p += ../src/utils/wpa_debug.o +OBJS_p += ../src/crypto/md5.o +OBJS_p += ../src/crypto/md4.o +OBJS_p += ../src/crypto/sha1.o +OBJS_p += ../src/crypto/des.o +OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o + +-include .config + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS_p += ../src/utils/os_$(CONFIG_OS).o +OBJS_c += ../src/utils/os_$(CONFIG_OS).o + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += ../src/utils/$(CONFIG_ELOOP).o + + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.o +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.o +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.o +endif + +ifdef CONFIG_NO_CONFIG_WRITE +CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS_d += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_WEXT +CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NL80211 +CFLAGS += -DCONFIG_DRIVER_NL80211 +OBJS_d += ../src/drivers/driver_nl80211.o +LIBS += -lnl +ifdef CONFIG_CLIENT_MLME +OBJS_d += ../src/drivers/radiotap.o +endif +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS_d += ../src/drivers/driver_prism54.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_HERMES +CFLAGS += -DCONFIG_DRIVER_HERMES +OBJS_d += ../src/drivers/driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS_d += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +CFLAGS += -DCONFIG_DRIVER_ATMEL +OBJS_d += ../src/drivers/driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +OBJS_d += ../src/drivers/driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_RALINK +CFLAGS += -DCONFIG_DRIVER_RALINK +OBJS_d += ../src/drivers/driver_ralink.o +endif + +ifdef CONFIG_DRIVER_BROADCOM +CFLAGS += -DCONFIG_DRIVER_BROADCOM +OBJS_d += ../src/drivers/driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +CFLAGS += -DCONFIG_DRIVER_IPW +OBJS_d += ../src/drivers/driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS_d += ../src/drivers/driver_bsd.o +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +endif + +ifdef CONFIG_DRIVER_NDIS +CFLAGS += -DCONFIG_DRIVER_NDIS +OBJS_d += ../src/drivers/driver_ndis.o +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +OBJS_d += ../src/drivers/driver_ndis_.o +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_WIRED +CFLAGS += -DCONFIG_DRIVER_WIRED +OBJS_d += ../src/drivers/driver_wired.o +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS_d += ../src/drivers/driver_test.o +endif + +ifdef CONFIG_DRIVER_OSX +CFLAGS += -DCONFIG_DRIVER_OSX +OBJS_d += ../src/drivers/driver_osx.o +LDFLAGS += -framework CoreFoundation +LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 +endif + +ifdef CONFIG_DRIVER_PS3 +CFLAGS += -DCONFIG_DRIVER_PS3 -m64 +OBJS_d += ../src/drivers/driver_ps3.o +LDFLAGS += -m64 +endif + +ifdef CONFIG_DRIVER_IPHONE +CFLAGS += -DCONFIG_DRIVER_IPHONE +OBJS_d += ../src/drivers/driver_iphone.o +OBJS_d += ../src/drivers/MobileApple80211.o +LIBS += -framework CoreFoundation +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +OBJS_d += ../src/drivers/driver_roboswitch.o +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_tls.so +else +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_tls.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_peap.so +else +CFLAGS += -DEAP_PEAP +OBJS += ../src/eap_peer/eap_peap.o +OBJS += ../src/eap_common/eap_peap_common.o +OBJS_h += ../src/eap_server/eap_peap.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ttls.so +else +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_peer/eap_ttls.o +OBJS_h += ../src/eap_server/eap_ttls.o +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += ../src/eap_peer/eap_md5.so +else +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_peer/eap_md5.o +OBJS_h += ../src/eap_server/eap_md5.o +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_mschapv2.so +EAPDYN += ../src/eap_peer/mschapv2.so +else +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_peer/eap_mschapv2.o +OBJS += ../src/eap_peer/mschapv2.o +OBJS_h += ../src/eap_server/eap_mschapv2.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gtc.so +else +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_peer/eap_gtc.o +OBJS_h += ../src/eap_server/eap_gtc.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_otp.so +else +CFLAGS += -DEAP_OTP +OBJS += ../src/eap_peer/eap_otp.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sim.so +else +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_peer/eap_sim.o +OBJS_h += ../src/eap_server/eap_sim.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_leap.so +else +CFLAGS += -DEAP_LEAP +OBJS += ../src/eap_peer/eap_leap.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_psk.so +else +CFLAGS += -DEAP_PSK +OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o +OBJS_h += ../src/eap_server/eap_psk.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += ../src/eap_peer/eap_aka.so +else +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_peer/eap_aka.o +OBJS_h += ../src/eap_server/eap_aka.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += ../src/eap_common/eap_sim_common.o +OBJS_h += ../src/eap_server/eap_sim_db.o +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_fast.so +EAPDYN += ../src/eap_common/eap_fast_common.o +else +CFLAGS += -DEAP_FAST +OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o +OBJS += ../src/eap_common/eap_fast_common.o +OBJS_h += ../src/eap_server/eap_fast.o +endif +TLS_FUNCS=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += ../src/eap_peer/eap_pax.so +else +CFLAGS += -DEAP_PAX +OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o +OBJS_h += ../src/eap_server/eap_pax.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sake.so +else +CFLAGS += -DEAP_SAKE +OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o +OBJS_h += ../src/eap_server/eap_sake.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gpsk.so +else +CFLAGS += -DEAP_GPSK +OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o +OBJS_h += ../src/eap_server/eap_gpsk.o +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +# EAP-WSC +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.o +OBJS += ../src/utils/uuid.o +OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +OBJS += ../src/wps/wps_attr_process.o +OBJS += ../src/wps/wps_dev_attr.o +OBJS += ../src/wps/wps_enrollee.o +OBJS += ../src/wps/wps_registrar.o +OBJS_h += ../src/eap_server/eap_wsc.o +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/httpread.o +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o +EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +else +CFLAGS += -DEAP_IKEV2 +OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o +OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +OBJS_h += ../src/eap_server/eap_ikev2.o +OBJS_h += ../src/eap_server/ikev2.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_vendor_test.so +else +CFLAGS += -DEAP_VENDOR_TEST +OBJS += ../src/eap_peer/eap_vendor_test.o +OBJS_h += ../src/eap_server/eap_vendor_test.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_peer/eap_tnc.o +OBJS += ../src/eap_peer/tncc.o +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +LIBS += -ldl +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += ../src/eapol_supp/eapol_supp_sm.o ../src/eap_peer/eap.o ../src/eap_common/eap_common.o ../src/eap_peer/eap_methods.o +ifdef CONFIG_DYNAMIC_EAP_METHODS +CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_EAP_SERVER +CFLAGS += -DEAP_SERVER +OBJS_h += ../src/eap_server/eap.o +OBJS_h += ../src/eap_server/eap_identity.o +OBJS_h += ../src/eap_server/eap_methods.o +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += ../src/utils/ip_addr.o +OBJS_h += ../src/radius/radius.o +OBJS_h += ../src/radius/radius_client.o +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += ../hostapd/eapol_sm.o +OBJS_h += ../hostapd/ieee802_1x.o +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += ../hostapd/wpa.o +OBJS_h += ../hostapd/wpa_auth_ie.o +ifdef CONFIG_IEEE80211R +OBJS_h += ../hostapd/wpa_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS_h += ../hostapd/peerkey.o +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += ../src/utils/pcsc_funcs.o +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += ../src/hlr_auc_gw/milenage.o +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif +ifeq ($(CONFIG_CRYPTO), internal) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif + + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += ../src/eap_peer/eap_tls_common.o +OBJS_h += ../src/eap_server/eap_tls_common.o +NEED_TLS_PRF=y +ifeq ($(CONFIG_TLS), openssl) +CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgcrypt -lgpg-error +LIBS_p += -lgcrypt +ifdef CONFIG_GNUTLS_EXTRA +CFLAGS += -DCONFIG_GNUTLS_EXTRA +LIBS += -lgnutls-extra +endif +endif +ifeq ($(CONFIG_TLS), schannel) +OBJS += ../src/crypto/tls_schannel.o +endif +ifeq ($(CONFIG_TLS), internal) +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o ../src/tls/tlsv1_client.o +OBJS += ../src/tls/tlsv1_client_write.o ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/asn1.o ../src/tls/x509v3.o +OBJS_p += ../src/tls/asn1.o +OBJS_p += ../src/crypto/rc4.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o +NEED_BASE64=y +NEED_TLS_PRF=y +CFLAGS += -DCONFIG_TLS_INTERNAL +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +ifeq ($(CONFIG_CRYPTO), internal) +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +ifeq ($(CONFIG_TLS), none) +OBJS += ../src/crypto/tls_none.o +CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +NEED_CRYPTO=y +else +OBJS += ../src/crypto/tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_CRYPTO=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +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) +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +endif +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/crypto_gnutls.o +OBJS_p += ../src/crypto/crypto_gnutls.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), schannel) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +OBJS_p += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +endif +endif +ifeq ($(CONFIG_TLS), none) +OBJS += ../src/crypto/crypto_none.o +OBJS_p += ../src/crypto/crypto_none.o +CONFIG_INTERNAL_SHA256=y +endif +else +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif + +ifdef CONFIG_INTERNAL_AES +CFLAGS += -DINTERNAL_AES +endif +ifdef CONFIG_INTERNAL_SHA1 +CFLAGS += -DINTERNAL_SHA1 +endif +ifdef CONFIG_INTERNAL_SHA256 +CFLAGS += -DINTERNAL_SHA256 +endif +ifdef CONFIG_INTERNAL_MD5 +CFLAGS += -DINTERNAL_MD5 +endif +ifdef CONFIG_INTERNAL_MD4 +CFLAGS += -DINTERNAL_MD4 +endif +ifdef CONFIG_INTERNAL_DES +CFLAGS += -DINTERNAL_DES +endif + +ifdef CONFIG_IEEE80211R +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +endif + +ifdef NEED_SHA256 +OBJS += ../src/crypto/sha256.o +CFLAGS += -DNEED_SHA256 +endif + +ifdef CONFIG_WIRELESS_EXTENSION +CFLAGS += -DCONFIG_WIRELESS_EXTENSION +OBJS_d += ../src/drivers/driver_wext.o +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +OBJS += ctrl_iface_dbus.o ctrl_iface_dbus_handlers.o dbus_dict_helpers.o +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +LIBS += $(DBUS_LIBS) +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) +DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) +DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) +ifeq ($(DBUS_VERSION_MAJOR),) +DBUS_VERSION_MAJOR=0 +endif +ifeq ($(DBUS_VERSION_MINOR),) +DBUS_VERSION_MINOR=0 +endif +DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) +DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) +CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_READLINE +CFLAGS += -DCONFIG_READLINE +LIBS_c += -lncurses -lreadline +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += ../src/rsn_supp/wpa_ft.o +endif + +ifndef CONFIG_NO_WPA +OBJS += ../src/rsn_supp/wpa.o +OBJS += ../src/rsn_supp/preauth.o +OBJS += ../src/rsn_supp/pmksa_cache.o +OBJS += ../src/rsn_supp/peerkey.o +OBJS += ../src/rsn_supp/wpa_ie.o +OBJS += ../src/common/wpa_common.o +NEED_AES=y +else +CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_NO_WPA_PASSPHRASE +CFLAGS += -DCONFIG_NO_PBKDF2 +endif + +ifdef CONFIG_NO_AES_EXTRAS +CFLAGS += -DCONFIG_NO_AES_WRAP +CFLAGS += -DCONFIG_NO_AES_CTR -DCONFIG_NO_AES_OMAC1 +CFLAGS += -DCONFIG_NO_AES_EAX -DCONFIG_NO_AES_CBC +CFLAGS += -DCONFIG_NO_AES_ENCRYPT +CFLAGS += -DCONFIG_NO_AES_ENCRYPT_BLOCK +endif + +ifdef NEED_AES +OBJS += ../src/crypto/aes_wrap.o ../src/crypto/aes.o +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +endif + +ifndef NEED_FIPS186_2_PRF +CFLAGS += -DCONFIG_NO_FIPS186_2_PRF +endif + +ifndef NEED_T_PRF +CFLAGS += -DCONFIG_NO_T_PRF +endif + +ifndef NEED_TLS_PRF +CFLAGS += -DCONFIG_NO_TLS_PRF +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef CONFIG_CLIENT_MLME +OBJS += mlme.o ../src/common/ieee802_11_common.o +CFLAGS += -DCONFIG_CLIENT_MLME +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_FILE +CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +OBJS += ../src/drivers/scan_helpers.o + +OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.o +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o ../src/radius/radius.o ../src/radius/radius_client.o +OBJS_t += ../src/utils/ip_addr.o +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o +OBJS += $(CONFIG_MAIN).o + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o ../src/drivers/scan_helpers.o +OBJS_priv += $(OBJS_l2) +OBJS_priv += ../src/utils/os_$(CONFIG_OS).o +OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o +OBJS_priv += ../src/utils/common.o +OBJS_priv += ../src/utils/wpa_debug.o +OBJS_priv += ../src/utils/wpabuf.o +OBJS_priv += wpa_priv.o +ifdef CONFIG_DRIVER_TEST +OBJS_priv += ../src/crypto/sha1.o +OBJS_priv += ../src/crypto/md5.o +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += ../src/crypto/crypto_openssl.o +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += ../src/crypto/crypto_gnutls.o +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += ../src/crypto/crypto_libtomcrypt.o +else +OBJS_priv += ../src/crypto/crypto_internal.o +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += ../src/l2_packet/l2_packet_privsep.o +OBJS += ../src/drivers/driver_privsep.o +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) ../src/drivers/drivers.o +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += ../src/drivers/ndis_events.o +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +dynamic_eap_methods: $(EAPDYN) + +wpa_priv: $(OBJS_priv) + $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + +wpa_supplicant: .config $(OBJS) $(EXTRA_progs) + $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + +eapol_test: .config $(OBJS_t) + $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + +preauth_test: .config $(OBJS_t2) + $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + +wpa_passphrase: $(OBJS_p) + $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + +wpa_cli: $(OBJS_c) + $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + +link_test: $(OBJS) $(OBJS_h) tests/link_test.o + $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + +test_wpa: $(OBJS_wpa) $(OBJS_h) + $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + +OBJSa=../src/tls/asn1_test.o ../src/tls/asn1.o ../src/tls/x509v3.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_unix.o \ + ../src/crypto/crypto_$(CONFIG_CRYPTO).o ../src/crypto/md5.o ../src/crypto/sha1.o \ + ../src/crypto/rc4.o ../src/crypto/des.o ../src/crypto/aes_wrap.o \ + ../src/crypto/aes.o ../src/tls/bignum.o ../src/tls/rsa.o +asn1_test: $(OBJSa) + $(LDO) $(LDFLAGS) -o asn1_test $(OBJSa) + +OBJSx=tests/test_x509v3.o ../src/tls/asn1.o ../src/tls/x509v3.o \ + ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_unix.o \ + ../src/crypto/crypto_$(CONFIG_CRYPTO).o \ + ../src/crypto/md5.o ../src/crypto/sha1.o ../src/crypto/aes.o \ + ../src/crypto/rc4.o ../src/crypto/des.o ../src/crypto/aes_wrap.o \ + ../src/crypto/sha256.o \ + ../src/tls/bignum.o ../src/tls/rsa.o +test_x509v3: $(OBJSx) + $(LDO) $(LDFLAGS) -o test_x509v3 $(OBJSx) + +win_if_list: win_if_list.c + $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + +eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_psk_register=eap_peer_method_dynamic_init + +eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_pax_register=eap_peer_method_dynamic_init + +eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_sake_register=eap_peer_method_dynamic_init + +eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_wsc_register=eap_peer_method_dynamic_init + +eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_ikev2_register=eap_peer_method_dynamic_init + +%.so: %.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ + -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init + + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ +eapol_test.exe: eapol_test + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +wpa_gui/Makefile: + qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro + +wpa_gui: wpa_gui/Makefile + $(MAKE) -C wpa_gui + +wpa_gui-qt4/Makefile: + qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4: wpa_gui-qt4/Makefile + $(MAKE) -C wpa_gui-qt4 + +TEST_MS_FUNCS_OBJS = ../src/crypto/crypto_openssl.o ../src/crypto/sha1.o ../src/crypto/md5.o \ + ../src/utils/os_unix.o ../src/crypto/rc4.o tests/test_ms_funcs.o +test-ms_funcs: $(TEST_MS_FUNCS_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MS_FUNCS_OBJS) $(LIBS) -lcrypto + ./test-ms_funcs + rm test-ms_funcs + +TEST_SHA1_OBJS = ../src/crypto/sha1.o ../src/crypto/md5.o tests/test_sha1.o #../src/crypto/crypto_openssl.o +test-sha1: $(TEST_SHA1_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_SHA1_OBJS) $(LIBS) + ./test-sha1 + rm test-sha1 + +TEST_SHA256_OBJS = ../src/crypto/sha256.o ../src/crypto/md5.o tests/test_sha256.o ../src/utils/os_unix.o ../src/crypto/crypto_openssl.o +test-sha256: $(TEST_SHA256_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_SHA256_OBJS) $(LIBS) + ./test-sha256 + rm test-sha256 + +TEST_AES_OBJS = ../src/crypto/aes_wrap.o ../src/crypto/aes.o tests/test_aes.o +test-aes: $(TEST_AES_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_AES_OBJS) $(LIBS) + ./test-aes + rm test-aes + +TEST_EAP_SIM_COMMON_OBJS = ../src/crypto/sha1.o ../src/crypto/md5.o \ + ../src/crypto/aes_wrap.o ../src/utils/common.o ../src/utils/os_unix.o \ + ../src/utils/wpa_debug.o ../src/crypto/aes.o \ + tests/test_eap_sim_common.o +test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_AES_OBJS) $(LIBS) + ./test-eap_sim_common + rm test-eap_sim_common + +TEST_MD4_OBJS = ../src/crypto/md4.o tests/test_md4.o #../src/crypto/crypto_openssl.o +test-md4: $(TEST_MD4_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MD4_OBJS) $(LIBS) + ./test-md4 + rm test-md4 + +TEST_MD5_OBJS = ../src/crypto/md5.o tests/test_md5.o #../src/crypto/crypto_openssl.o +test-md5: $(TEST_MD5_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MD5_OBJS) $(LIBS) + ./test-md5 + rm test-md5 + +tests: test-ms_funcs test-sha1 test-aes test-eap_sim_common test-md4 test-md5 + +clean: + $(MAKE) -C ../src clean + rm -f core *~ *.o *.d eap_*.so $(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 + (cd ..; doxygen wpa_supplicant/doc/doxygen.full; cd wpa_supplicant) + $(MAKE) -C doc/latex + cp doc/latex/refman.pdf wpa_supplicant-devel.pdf + +docs-fast: docs-pics + (cd ..; doxygen wpa_supplicant/doc/doxygen.fast; cd wpa_supplicant) + +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/wpa_supplicant/README b/wpa_supplicant/README new file mode 100644 index 0000000000000..2b94c2309d8ad --- /dev/null +++ b/wpa_supplicant/README @@ -0,0 +1,1028 @@ +WPA Supplicant +============== + +Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + + +License +------- + +GPL v2: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed, used, and modified +under the terms of BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") + Following authentication methods are supported with an integrate IEEE 802.1X + Supplicant: + * EAP-TLS + * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) + * EAP-PEAP/TLS (both PEAPv0 and PEAPv1) + * EAP-PEAP/GTC (both PEAPv0 and PEAPv1) + * EAP-PEAP/OTP (both PEAPv0 and PEAPv1) + * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) + * EAP-TTLS/EAP-MD5-Challenge + * EAP-TTLS/EAP-GTC + * EAP-TTLS/EAP-OTP + * EAP-TTLS/EAP-MSCHAPv2 + * EAP-TTLS/EAP-TLS + * EAP-TTLS/MSCHAPv2 + * EAP-TTLS/MSCHAP + * EAP-TTLS/PAP + * EAP-TTLS/CHAP + * EAP-SIM + * EAP-AKA + * EAP-PSK + * EAP-PAX + * EAP-SAKE + * EAP-IKEv2 + * EAP-GPSK + * LEAP (note: requires special support from the driver for IEEE 802.11 + authentication) + (following methods are supported, but since they do not generate keying + material, they cannot be used with WPA or IEEE 802.1X WEP keying) + * EAP-MD5-Challenge + * EAP-MSCHAPv2 + * EAP-GTC + * EAP-OTP +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i) + * pre-authentication + * PMKSA caching + +Supported TLS/crypto libraries: +- OpenSSL (default) +- GnuTLS + +Internal TLS/crypto implementation (optional): +- can be used in place of an external TLS/crypto library +- TLSv1 +- X.509 certificate processing +- PKCS #1 +- ASN.1 +- RSA +- bignum +- minimal size (ca. 50 kB binary, parts of which are already needed for WPA; + TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86) + + +Requirements +------------ + +Current hardware/software requirements: +- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer +- FreeBSD 6-CURRENT +- NetBSD-current +- Microsoft Windows with WinPcap (at least WinXP, may work with other versions) +- drivers: + Linux drivers that support WPA/WPA2 configuration with the generic + Linux wireless extensions (WE-18 or newer). Even though there are + number of driver specific interface included in wpa_supplicant, please + note that Linux drivers are moving to use generic wireless extensions + and driver_wext (-Dwext on wpa_supplicant command line) should be the + default option to start with before falling back to driver specific + interface. + + Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) + (http://hostap.epitest.fi/) + Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + Linuxant DriverLoader (http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA. + + Agere Systems Inc. Linux Driver + (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. + + madwifi driver for cards based on Atheros chip set (ar521x) + (http://sourceforge.net/projects/madwifi/) + Please note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root directory + (CFLAGS += -I../madwifi/wpa line in example defconfig). + + ATMEL AT76C5XXx driver for USB and PCMCIA cards + (http://atmelwlandriver.sourceforge.net/). + + Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with + Windows NDIS driver. + + Broadcom wl.o driver (old version only) + This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. + However, it is proprietary driver that is not publicly available + except for couple of exceptions, mainly Broadcom-based APs/wireless + routers that use Linux. The driver binary can be downloaded, e.g., + from Linksys support site (http://www.linksys.com/support/gpl.asp) + for Linksys WRT54G. The GPL tarball includes cross-compiler and + the needed header file, wlioctl.h, for compiling wpa_supplicant. + This driver support in wpa_supplicant is expected to work also with + other devices based on Broadcom driver (assuming the driver includes + client mode support). Please note that the newer Broadcom driver + ("hybrid Linux driver") supports Linux wireless extensions and does + not need (or even work) with the specific driver wrapper. Use -Dwext + with that driver. + + Intel ipw2100 driver + (http://sourceforge.net/projects/ipw2100/) + + Intel ipw2200 driver + (http://sourceforge.net/projects/ipw2200/) + + In theory, any driver that supports Linux wireless extensions can be + used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in + configuration file. + + Wired Ethernet drivers (with ap_scan=0) + + BSD net80211 layer (e.g., Atheros driver) + At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current. + + Windows NDIS + The current Windows port requires WinPcap (http://winpcap.polito.it/). + See README-Windows.txt for more information. + +wpa_supplicant was designed to be portable for different drivers and +operating systems. Hopefully, support for more wlan cards and OSes will be +added in the future. See developer's documentation +(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the +design of wpa_supplicant and porting to other drivers. One main goal +is to add full WPA/WPA2 support to Linux wireless extensions to allow +new drivers to be supported without having to implement new +driver-specific interface code in wpa_supplicant. + +Optional libraries for layer2 packet processing: +- libpcap (tested with 0.7.2, most relatively recent versions assumed to work, + this is likely to be available with most distributions, + http://tcpdump.org/) +- libdnet (tested with v1.4, most versions assumed to work, + http://libdnet.sourceforge.net/) + +These libraries are _not_ used in the default Linux build. Instead, +internal Linux specific implementation is used. libpcap/libdnet are +more portable and they can be used by adding CONFIG_L2_PACKET=pcap into +.config. They may also be selected automatically for other operating +systems. In case of Windows builds, WinPcap is used by default +(CONFIG_L2_PACKET=winpcap). + + +Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: +- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to + work with most relatively recent versions; this is likely to be + available with most distributions, http://www.openssl.org/) +- GnuTLS +- internal TLSv1 implementation + +TLS options for EAP-FAST: +- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied + (i.e., the default OpenSSL package does not include support for + extensions needed for EAP-FAST) +- internal TLSv1 implementation + +One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or +EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP +implementation. A configuration file, .config, for compilation is +needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5, +EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so +they should only be enabled if testing the EAPOL/EAP state +machines. However, there can be used as inner authentication +algorithms with EAP-PEAP and EAP-TTLS. + +See Building and installing section below for more detailed +information about the wpa_supplicant build time configuration. + + + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proven to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access<TM> (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + + + +wpa_supplicant +-------------- + +wpa_supplicant is an implementation of the WPA Supplicant component, +i.e., the part that runs in the client stations. It implements WPA key +negotiation with a WPA Authenticator and EAP authentication with +Authentication Server. In addition, it controls the roaming and IEEE +802.11 authentication/association of the wlan driver. + +wpa_supplicant is designed to be a "daemon" program that runs in the +background and acts as the backend component controlling the wireless +connection. wpa_supplicant supports separate frontend programs and an +example text-based frontend, wpa_cli, is included with wpa_supplicant. + +Following steps are used when associating with an AP using WPA: + +- wpa_supplicant requests the kernel driver to scan neighboring BSSes +- wpa_supplicant selects a BSS based on its configuration +- wpa_supplicant requests the kernel driver to associate with the chosen + BSS +- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP + authentication with the authentication server (proxied by the + Authenticator in the AP) +- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant +- If WPA-PSK: wpa_supplicant uses PSK as the master session key +- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake + with the Authenticator (AP) +- wpa_supplicant configures encryption keys for unicast and broadcast +- normal data packets can be transmitted and received + + + +Building and installing +----------------------- + +In order to be able to build wpa_supplicant, you will first need to +select which parts of it will be included. This is done by creating a +build time configuration file, .config, in the wpa_supplicant root +directory. Configuration options are text lines using following +format: CONFIG_<option>=y. Lines starting with # are considered +comments and are ignored. See defconfig file for an example configuration +and a list of available options and additional notes. + +The build time configuration can be used to select only the needed +features and limit the binary size and requirements for external +libraries. The main configuration parts are the selection of which +driver interfaces (e.g., hostap, madwifi, ..) and which authentication +methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. + +Following build time configuration options are used to control IEEE +802.1X/EAPOL and EAP state machines and all EAP methods. Including +TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL +library for TLS implementation. Alternatively, GnuTLS or the internal +TLSv1 implementation can be used for TLS functionaly. + +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_SAKE=y +CONFIG_EAP_GPSK=y +CONFIG_EAP_PAX=y +CONFIG_EAP_LEAP=y +CONFIG_EAP_IKEV2=y + +Following option can be used to include GSM SIM/USIM interface for GSM/UMTS +authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +(http://www.linuxnet.com/) for smart card access. + +CONFIG_PCSC=y + +Following options can be added to .config to select which driver +interfaces are included. 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_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_RALINK=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y + +Following example includes all features and driver interfaces that are +included in the wpa_supplicant package: + +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y +CONFIG_WIRELESS_EXTENSION=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_SAKE=y +CONFIG_EAP_GPSK=y +CONFIG_EAP_PAX=y +CONFIG_EAP_LEAP=y +CONFIG_EAP_IKEV2=y +CONFIG_PCSC=y + +EAP-PEAP and EAP-TTLS will automatically include configured EAP +methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection. + + +After you have created a configuration file, you can build +wpa_supplicant and wpa_cli with 'make' command. You may then install +the binaries to a suitable system directory, e.g., /usr/local/bin. + +Example commands: + +# build wpa_supplicant and wpa_cli +make +# install binaries (this may need root privileges) +cp wpa_cli wpa_supplicant /usr/local/bin + + +You will need to make a configuration file, e.g., +/etc/wpa_supplicant.conf, with network configuration for the networks +you are going to use. Configuration file section below includes +explanation fo the configuration file format and includes various +examples. Once the configuration is ready, you can test whether the +configuration work by first running wpa_supplicant with following +command to start it on foreground with debugging enabled: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + +Assuming everything goes fine, you can start using following command +to start wpa_supplicant on background without debugging: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + +Please note that if you included more than one driver interface in the +build time configuration (.config), you may need to specify which +interface to use by including -D<driver name> option on the command +line. See following section for more details on command line options +for wpa_supplicant. + + + +Command line options +-------------------- + +usage: + wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \ + -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \ + [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \ + [-p<driver_param>] [-b<br_ifname>] ...] + +options: + -b = optional bridge interface name + -B = run daemon in the background + -c = Configuration file + -C = ctrl_interface parameter (only used if -c is not) + -i = interface name + -d = increase debugging verbosity (-dd even more) + -D = driver name + -f = Log output to default log location (normally /tmp) + -g = global ctrl_interface + -K = include keys (passwords, etc.) in debug output + -t = include timestamp in debug messages + -h = show this help text + -L = show license (GPL and BSD) + -p = driver parameters + -P = PID file + -q = decrease debugging verbosity (-qq even less) + -u = enable DBus control interface + -v = show version + -w = wait for interface to be added, if needed + -W = wait for a control interface monitor before starting + -N = start describing new interface + +drivers: + hostap = Host AP driver (Intersil Prism2/2.5/3) [default] + (this can also be used with Linuxant DriverLoader) + hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) + madwifi = MADWIFI 802.11 support (Atheros, etc.) + atmel = ATMEL AT76C5XXx (USB, PCMCIA) + wext = Linux wireless extensions (generic) + ralink = Ralink Client driver + ndiswrapper = Linux ndiswrapper + broadcom = Broadcom wl.o driver + ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer) + wired = wpa_supplicant wired Ethernet driver + roboswitch = wpa_supplicant Broadcom switch driver + bsd = BSD 802.11 support (Atheros, etc.) + ndis = Windows NDIS driver + +In most common cases, wpa_supplicant is started with + +wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0 + +This makes the process fork into background. + +The easiest way to debug problems, and to get debug log for bug +reports, is to start wpa_supplicant on foreground with debugging +enabled: + +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d + + +wpa_supplicant can control multiple interfaces (radios) either by +running one process for each interface separately or by running just +one process and list of options at command line. Each interface is +separated with -N argument. As an example, following command would +start wpa_supplicant for two interfaces: + +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi + + +If the interface is added in a Linux bridge (e.g., br0), the bridge +interface needs to be configured to wpa_supplicant in addition to the +main interface: + +wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0 + + +Configuration file +------------------ + +wpa_supplicant is configured using a text file that lists all accepted +networks and security policies, including pre-shared keys. See +example configuration file, wpa_supplicant.conf, for detailed +information about the configuration format and supported fields. + +Changes to configuration file can be reloaded be sending SIGHUP signal +to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly, +reloading can be triggered with 'wpa_cli reconfigure' command. + +Configuration file can include one or more network blocks, e.g., one +for each used SSID. wpa_supplicant will automatically select the best +betwork based on the order of network blocks in the configuration +file, network security level (WPA/WPA2 is preferred), and signal +strength. + +Example configuration files for some common configurations: + +1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work + network + +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} + + +2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel + (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series) + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} + + +3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the + unencrypted use. Real identity is sent only within an encrypted TLS tunnel. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} + + +4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and + broadcast); use EAP-TLS for authentication + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +5) Catch all example that allows more or less all configuration modes. The + configuration options are used based on what security policy is used in the + selected SSID. This is mostly for testing and is not recommended for normal + use. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} + + +6) Authentication for wired Ethernet. This can be used with 'wired' or + 'roboswitch' interface (-Dwired or -Droboswitch on command line). + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +ap_scan=0 +network={ + key_mgmt=IEEE8021X + eap=MD5 + identity="user" + password="password" + eapol_flags=0 +} + + + +Certificates +------------ + +Some EAP authentication methods require use of certificates. EAP-TLS +uses both server side and client certificates whereas EAP-PEAP and +EAP-TTLS only require the server side certificate. When client +certificate is used, a matching private key file has to also be +included in configuration. If the private key uses a passphrase, this +has to be configured in wpa_supplicant.conf ("private_key_passwd"). + +wpa_supplicant supports X.509 certificates in PEM and DER +formats. User certificate and private key can be included in the same +file. + +If the user certificate and private key is received in PKCS#12/PFX +format, they need to be converted to suitable PEM/DER format for +wpa_supplicant. This can be done, e.g., with following commands: + +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys + + + +wpa_cli +------- + +wpa_cli is a text-based frontend program for interacting with +wpa_supplicant. It is used to query current status, change +configuration, trigger events, and request interactive user input. + +wpa_cli can show the current authentication status, selected security +mode, dot11 and dot1x MIBs, etc. In addition, it can configure some +variables like EAPOL state machine parameters and trigger events like +reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user +interface to request authentication information, like username and +password, if these are not included in the configuration. This can be +used to implement, e.g., one-time-passwords or generic token card +authentication where the authentication is based on a +challenge-response that uses an external device for generating the +response. + +The control interface of wpa_supplicant can be configured to allow +non-root user access (ctrl_interface_group in the configuration +file). This makes it possible to run wpa_cli with a normal user +account. + +wpa_cli supports two modes: interactive and command line. Both modes +share the same command set and the main difference is in interactive +mode providing access to unsolicited messages (event messages, +username/password requests). + +Interactive mode is started when wpa_cli is executed without including +the command as a command line parameter. Commands are then entered on +the wpa_cli prompt. In command line mode, the same commands are +entered as command line arguments for wpa_cli. + + +Interactive authentication parameters request + +When wpa_supplicant need authentication parameters, like username and +password, which are not present in the configuration file, it sends a +request message to all attached frontend programs, e.g., wpa_cli in +interactive mode. wpa_cli shows these requests with +"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or +OTP (one-time-password). <id> is a unique identifier for the current +network. <text> is description of the request. In case of OTP request, +it includes the challenge from the authentication server. + +The reply to these requests can be given with 'identity', 'password', +and 'otp' commands. <id> needs to be copied from the the matching +request. 'password' and 'otp' commands can be used regardless of +whether the request was for PASSWORD or OTP. The main difference +between these two commands is that values given with 'password' are +remembered as long as wpa_supplicant is running whereas values given +with 'otp' are used only once and then forgotten, i.e., wpa_supplicant +will ask frontend for a new value for every use. This can be used to +implement one-time-password lists and generic token card -based +authentication. + +Example request for password and a matching reply: + +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword + +Example request for generic token card challenge-response: + +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 + + +wpa_cli commands + + status = get current WPA/EAPOL/EAP status + mib = get MIB variables (dot1x, dot11) + help = show this usage help + interface [ifname] = show interfaces/select interface + level <debug level> = change debug level + license = show full wpa_cli license + logoff = IEEE 802.1X EAPOL state machine logoff + logon = IEEE 802.1X EAPOL state machine logon + set = set variables (shows list of variables when run without arguments) + pmksa = show PMKSA cache + reassociate = force reassociation + reconfigure = force wpa_supplicant to re-read its configuration file + preauthenticate <BSSID> = force preauthentication + identity <network id> <identity> = configure identity for an SSID + password <network id> <password> = configure password for an SSID + pin <network id> <pin> = configure pin for an SSID + otp <network id> <password> = configure one-time-password for an SSID + passphrase <network id> <passphrase> = configure private key passphrase + for an SSID + bssid <network id> <BSSID> = set preferred BSSID for an SSID + list_networks = list configured networks + select_network <network id> = select a network (disable others) + enable_network <network id> = enable a network + disable_network <network id> = disable a network + add_network = add a network + remove_network <network id> = remove a network + set_network <network id> <variable> <value> = set network variables (shows + list of variables when run without arguments) + get_network <network id> <variable> = get network variables + save_config = save the current configuration + disconnect = disconnect and wait for reassociate command before connecting + scan = request new BSS scan + scan_results = get latest scan results + get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies + terminate = terminate wpa_supplicant + quit = exit wpa_cli + + +wpa_cli command line options + +wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] [-a<action file>] \ + [-P<pid file>] [-g<global ctrl>] [command..] + -h = help (show this usage text) + -v = shown version information + -a = run in daemon mode executing the action file based on events from + wpa_supplicant + -B = run a daemon in the background + default path: /var/run/wpa_supplicant + default interface: first interface found in socket path + + +Using wpa_cli to run external program on connect/disconnect +----------------------------------------------------------- + +wpa_cli can used to run external programs whenever wpa_supplicant +connects or disconnects from a network. This can be used, e.g., to +update network configuration and/or trigget DHCP client to update IP +addresses, etc. + +One wpa_cli process in "action" mode needs to be started for each +interface. For example, the following command starts wpa_cli for the +default ingterface (-i can be used to select the interface in case of +more than one interface being used at the same time): + +wpa_cli -a/sbin/wpa_action.sh -B + +The action file (-a option, /sbin/wpa_action.sh in this example) will +be executed whenever wpa_supplicant completes authentication (connect +event) or detects disconnection). The action script will be called +with two command line arguments: interface name and event (CONNECTED +or DISCONNECTED). If the action script needs to get more information +about the current network, it can use 'wpa_cli status' to query +wpa_supplicant for more information. + +Following example can be used as a simple template for an action +script: + +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +if [ "$CMD" == "CONNECTED" ]; then + SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=` + # configure network, signal DHCP client, etc. +fi + +if [ "$CMD" == "DISCONNECTED" ]; then + # remove network configuration, if needed +fi + + + +Integrating with pcmcia-cs/cardmgr scripts +------------------------------------------ + +wpa_supplicant needs to be running when using a wireless network with +WPA. It can be started either from system startup scripts or from +pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be +completed before data frames can be exchanged, so wpa_supplicant +should be started before DHCP client. + +For example, following small changes to pcmcia-cs scripts can be used +to enable WPA support: + +Add MODE="Managed" and WPA="y" to the network scheme in +/etc/pcmcia/wireless.opts. + +Add the following block to the end of 'start' action handler in +/etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf \ + -i$DEVICE + fi + +Add the following block to the end of 'stop' action handler (may need +to be separated from other actions) in /etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant + fi + +This will make cardmgr start wpa_supplicant when the card is plugged +in. + + + +Dynamic interface add and operation without configuration files +--------------------------------------------------------------- + +wpa_supplicant can be started without any configuration files or +network interfaces. When used in this way, a global (i.e., per +wpa_supplicant process) control interface is used to add and remove +network interfaces. Each network interface can then be configured +through a per-network interface control interface. For example, +following commands show how to start wpa_supplicant without any +network interfaces and then add a network interface and configure a +network (SSID): + +# Start wpa_supplicant in the background +wpa_supplicant -g/var/run/wpa_supplicant-global -B + +# Add a new interface (wlan0, no configuration file, driver=wext, and +# enable control interface) +wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \ + "" wext /var/run/wpa_supplicant + +# Configure a network using the newly added network interface: +wpa_cli -iwlan0 add_network +wpa_cli -iwlan0 set_network 0 ssid '"test"' +wpa_cli -iwlan0 set_network 0 key_mgmt WPA-PSK +wpa_cli -iwlan0 set_network 0 psk '"12345678"' +wpa_cli -iwlan0 set_network 0 pairwise TKIP +wpa_cli -iwlan0 set_network 0 group TKIP +wpa_cli -iwlan0 set_network 0 proto WPA +wpa_cli -iwlan0 enable_network 0 + +# At this point, the new network interface should start trying to associate +# with the WPA-PSK network using SSID test. + +# Remove network interface +wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0 + + +Privilege separation +-------------------- + +To minimize the size of code that needs to be run with root privileges +(e.g., to control wireless interface operation), wpa_supplicant +supports optional privilege separation. If enabled, this separates the +privileged operations into a separate process (wpa_priv) while leaving +rest of the code (e.g., EAP authentication and WPA handshakes) into an +unprivileged process (wpa_supplicant) that can be run as non-root +user. Privilege separation restricts the effects of potential software +errors by containing the majority of the code in an unprivileged +process to avoid full system compromise. + +Privilege separation is not enabled by default and it can be enabled +by adding CONFIG_PRIVSEP=y to the build configuration (.config). When +enabled, the privileged operations (driver wrapper and l2_packet) are +linked into a separate daemon program, wpa_priv. The unprivileged +program, wpa_supplicant, will be built with a special driver/l2_packet +wrappers that communicate with the privileged wpa_priv process to +perform the needed operations. wpa_priv can control what privileged +are allowed. + +wpa_priv needs to be run with network admin privileges (usually, root +user). It opens a UNIX domain socket for each interface that is +included on the command line; any other interface will be off limits +for wpa_supplicant in this kind of configuration. After this, +wpa_supplicant can be run as a non-root user (e.g., all standard users +on a laptop or as a special non-privileged user account created just +for this purpose to limit access to user files even further). + + +Example configuration: +- create user group for users that are allowed to use wpa_supplicant + ('wpapriv' in this example) and assign users that should be able to + use wpa_supplicant into that group +- create /var/run/wpa_priv directory for UNIX domain sockets and control + user access by setting it accessible only for the wpapriv group: + mkdir /var/run/wpa_priv + chown root:wpapriv /var/run/wpa_priv + chmod 0750 /var/run/wpa_priv +- start wpa_priv as root (e.g., from system startup scripts) with the + enabled interfaces configured on the command line: + wpa_priv -B -P /var/run/wpa_priv.pid wext:ath0 +- run wpa_supplicant as non-root with a user that is in wpapriv group: + wpa_supplicant -i ath0 -c wpa_supplicant.conf + +wpa_priv does not use the network interface before wpa_supplicant is +started, so it is fine to include network interfaces that are not +available at the time wpa_priv is started. As an alternative, wpa_priv +can be started when an interface is added (hotplug/udev/etc. scripts). +wpa_priv can control multiple interface with one process, but it is +also possible to run multiple wpa_priv processes at the same time, if +desired. diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS new file mode 100644 index 0000000000000..7fd358dbec667 --- /dev/null +++ b/wpa_supplicant/README-WPS @@ -0,0 +1,184 @@ +wpa_supplicant and Wi-Fi Protected Setup (WPS) +============================================== + +This document describes how the WPS implementation in wpa_supplicant +can be configured and how an external component on the client (e.g., +management GUI) is used to enable WPS enrollment and registrar +registration. + + +Introduction to WPS +------------------- + +Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a +wireless network. It allows automated generation of random keys (WPA +passphrase/PSK) and configuration of an access point and client +devices. WPS includes number of methods for setting up connections +with PIN method and push-button configuration (PBC) being the most +commonly deployed options. + +While WPS can enable more home networks to use encryption in the +wireless network, it should be noted that the use of the PIN and +especially PBC mechanisms for authenticating the initial key setup is +not very secure. As such, use of WPS may not be suitable for +environments that require secure network access without chance for +allowing outsiders to gain access during the setup phase. + +WPS uses following terms to describe the entities participating in the +network setup: +- access point: the WLAN access point +- Registrar: a device that control a network and can authorize + addition of new devices); this may be either in the AP ("internal + Registrar") or in an external device, e.g., a laptop, ("external + Registrar") +- Enrollee: a device that is being authorized to use the network + +It should also be noted that the AP and a client device may change +roles (i.e., AP acts as an Enrollee and client device as a Registrar) +when WPS is used to configure the access point. + + +More information about WPS is available from Wi-Fi Alliance: +http://www.wi-fi.org/wifi-protected-setup + + +wpa_supplicant implementation +----------------------------- + +wpa_supplicant includes an optional WPS component that can be used as +an Enrollee to enroll new network credential or as a Registrar to +configure an AP. The current version of wpa_supplicant does not +support operation as an external WLAN Management Registrar for adding +new client devices or configuring the AP over UPnP. + + +wpa_supplicant configuration +---------------------------- + +WPS is an optional component that needs to be enabled in +wpa_supplicant build configuration (.config). Here is an example +configuration that includes WPS support and Linux wireless extensions +-based driver interface: + +CONFIG_DRIVER_WEXT=y +CONFIG_EAP=y +CONFIG_WPS=y + + +WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for +the device. This is configured in the runtime configuration for +wpa_supplicant (if not set, UUID will be generated based on local MAC +address): + +# example UUID for WPS +uuid=12345678-9abc-def0-1234-56789abcdef0 + +The network configuration blocks needed for WPS are added +automatically based on control interface commands, so they do not need +to be added explicitly in the configuration file. + +WPS registration will generate new network blocks for the acquired +credentials. If these are to be stored for future use (after +restarting wpa_supplicant), wpa_supplicant will need to be configured +to allow configuration file updates: + +update_config=1 + + + +External operations +------------------- + +WPS requires either a device PIN code (usually, 8-digit number) or a +pushbutton event (for PBC) to allow a new WPS Enrollee to join the +network. wpa_supplicant uses the control interface as an input channel +for these events. + +If the client device has a display, a random PIN has to be generated +for each WPS registration session. wpa_supplicant can do this with a +control interface request, e.g., by calling wpa_cli: + +wpa_cli wps_pin any + +This will return the generated 8-digit PIN which will then need to be +entered at the Registrar to complete WPS registration. At that point, +the client will be enrolled with credentials needed to connect to the +AP to access the network. + + +If the client device does not have a display that could show the +random PIN, a hardcoded PIN that is printed on a label can be +used. wpa_supplicant is notified this with a control interface +request, e.g., by calling wpa_cli: + +wpa_cli wps_pin any 12345670 + +This starts the WPS negotiation in the same way as above with the +generated PIN. + + +If the client design wants to support optional WPS PBC mode, this can +be enabled by either a physical button in the client device or a +virtual button in the user interface. The PBC operation requires that +a button is also pressed at the AP/Registrar at about the same time (2 +minute window). wpa_supplicant is notified of the local button event +over the control interface, e.g., by calling wpa_cli: + +wpa_cli wps_pbc + +At this point, the AP/Registrar has two minutes to complete WPS +negotiation which will generate a new WPA PSK in the same way as the +PIN method described above. + + +If the client wants to operation in the Registrar role to configure an +AP, wpa_supplicant is notified over the control interface, e.g., with +wpa_cli: + +wpa_cli wps_reg <AP BSSID> <AP PIN> +(example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670) + + +Scanning +-------- + +Scan results ('wpa_cli scan_results' or 'wpa_cli bss <idx>') include a +flags field that is used to indicate whether the BSS support WPS. If +the AP support WPS, but has not recently activated a Registrar, [WPS] +flag will be included. If PIN method has been recently selected, +[WPS-PIN] is shown instead. Similarly, [WPS-PBC] is shown if PBC mode +is in progress. GUI programs can use these as triggers for suggesting +a guided WPS configuration to the user. In addition, control interface +monitor events WPS-AP-AVAILABLE{,-PBC,-PIN} can be used to find out if +there are WPS enabled APs in scan results without having to go through +all the details in the GUI. These notification could be used, e.g., to +suggest possible WPS connection to the user. + + +wpa_gui +------- + +wpa_gui-qt4 directory contains a sample GUI that shows an example of +how WPS support can be integrated into the GUI. Its main window has a +WPS tab that guides user through WPS registration with automatic AP +selection. In addition, it shows how WPS can be started manually by +selecting an AP from scan results. + + +Credential processing +--------------------- + +By default, wpa_supplicant processes received credentials and updates +its configuration internally. However, it is possible to +control these operations from external programs, if desired. + +This internal processing can be disabled with wps_cred_processing=1 +option. When this is used, an external program is responsible for +processing the credential attributes and updating wpa_supplicant +configuration based on them. + +Following control interface messages are sent out for external programs: + +WPS-CRED-RECEIVED <hexdump of Credential attribute(s)> +For example: +<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727 diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt new file mode 100644 index 0000000000000..292223d2d5fea --- /dev/null +++ b/wpa_supplicant/README-Windows.txt @@ -0,0 +1,450 @@ +wpa_supplicant for Windows +========================== + +Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + +This product includes software developed by the OpenSSL Project +for use in the OpenSSL Toolkit (http://www.openssl.org/). This +product includes cryptographic software written by Eric Young +(eay@cryptsoft.com). + + +wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X +Supplicant on Windows. The current port requires that WinPcap +(http://winpcap.polito.it/) is installed for accessing packets and the +driver interface. Both release versions 3.0 and 3.1 are supported. + +The current port is still somewhat experimental. It has been tested +mainly on Windows XP (SP2) with limited set of NDIS drivers. In +addition, the current version has been reported to work with Windows +2000. + +All security modes have been verified to work (at least complete +authentication and successfully ping a wired host): +- plaintext +- static WEP / open system authentication +- static WEP / shared key authentication +- IEEE 802.1X with dynamic WEP keys +- WPA-PSK, TKIP, CCMP, TKIP+CCMP +- WPA-EAP, TKIP, CCMP, TKIP+CCMP +- WPA2-PSK, TKIP, CCMP, TKIP+CCMP +- WPA2-EAP, TKIP, CCMP, TKIP+CCMP + + +Binary version +-------------- + +Compiled binary version of the wpa_supplicant and additional tools is +available from http://w1.fi/wpa_supplicant/. These binaries can be +used after installing WinPcap. + +wpa_gui uses Qt 4 framework and may need additional dynamic libraries +(DLLs). These libraries are available from +http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip +You can copy the DLL files from this ZIP package into the same directory +with wpa_gui.exe to allow wpa_gui to be started. + + +Building wpa_supplicant with mingw +---------------------------------- + +The default build setup for wpa_supplicant is to use MinGW and +cross-compiling from Linux to MinGW/Windows. It should also be +possible to build this under Windows using the MinGW tools, but that +is not tested nor supported and is likely to require some changes to +the Makefile unless cygwin is used. + + +Building wpa_supplicant with MSVC +--------------------------------- + +wpa_supplicant can be built with Microsoft Visual C++ compiler. This +has been tested with Microsoft Visual C++ Toolkit 2003 and Visual +Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE +can also be used by creating a project that includes the files and +defines mentioned in nmake.mak. Example VS2005 solution and project +files are included in vs2005 subdirectory. This can be used as a +starting point for building the programs with VS2005 IDE. Visual Studio +2008 Express Edition is also able to use these project files. + +WinPcap development package is needed for the build and this can be +downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The +default nmake.mak expects this to be unpacked into C:\dev\WpdPack so +that Include and Lib directories are in this directory. The files can be +stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to +match with the selected directory. In case a project file in the IDE is +used, these Include and Lib directories need to be added to project +properties as additional include/library directories. + +OpenSSL source package can be downloaded from +http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and +installed following instructions in INSTALL.W32. Note that if EAP-FAST +support will be included in the wpa_supplicant, OpenSSL needs to be +patched to# support it openssl-0.9.8i-tls-extensions.patch. The example +nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but +this directory can be modified by changing OPENSSLDIR variable in +nmake.mak. + +If you do not need EAP-FAST support, you may also be able to use Win32 +binary installation package of OpenSSL from +http://www.slproweb.com/products/Win32OpenSSL.html instead of building +the library yourself. In this case, you will need to copy Include and +Lib directories in suitable directory, e.g., C:\dev\openssl for the +default nmake.mak. Copy {Win32OpenSSLRoot}\include into +C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with +files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib). +This will end up using dynamically linked OpenSSL (i.e., .dll files are +needed) for it. Alternative, you can copy files from +{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll +files needed). + + +Building wpa_supplicant for cygwin +---------------------------------- + +wpa_supplicant can be built for cygwin by installing the needed +development packages for cygwin. This includes things like compiler, +make, openssl development package, etc. In addition, developer's pack +for WinPcap (WPdpack.zip) from +http://winpcap.polito.it/install/default.htm is needed. + +.config file should enable only one driver interface, +CONFIG_DRIVER_NDIS. In addition, include directories may need to be +added to match the system. An example configuration is available in +defconfig. The library and include files for WinPcap will either need +to be installed in compiler/linker default directories or their +location will need to be adding to .config when building +wpa_supplicant. + +Othen than this, the build should be more or less identical to Linux +version, i.e., just run make after having created .config file. An +additional tool, win_if_list.exe, can be built by running "make +win_if_list". + + +Building wpa_gui +---------------- + +wpa_gui uses Qt application framework from Trolltech. It can be built +with the open source version of Qt4 and MinGW. Following commands can +be used to build the binary in the Qt 4 Command Prompt: + +# go to the root directory of wpa_supplicant source code +cd wpa_gui-qt4 +qmake -o Makefile wpa_gui.pro +make +# the wpa_gui.exe binary is created into 'release' subdirectory + + +Using wpa_supplicant for Windows +-------------------------------- + +wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to +Linux version, so instructions in README and example wpa_supplicant.conf +should be applicable for most parts. In addition, there is another +version of wpa_supplicant, wpasvc.exe, which can be used as a Windows +service and which reads its configuration from registry instead of +text file. + +When using access points in "hidden SSID" mode, ap_scan=2 mode need to +be used (see wpa_supplicant.conf for more information). + +Windows NDIS/WinPcap uses quite long interface names, so some care +will be needed when starting wpa_supplicant. Alternatively, the +adapter description can be used as the interface name which may be +easier since it is usually in more human-readable +format. win_if_list.exe can be used to find out the proper interface +name. + +Example steps in starting up wpa_supplicant: + +# win_if_list.exe +ifname: \Device\NPF_GenericNdisWanAdapter +description: Generic NdisWan adapter + +ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2} +description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler) + +ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211} +description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler) + + +Since the example configuration used Atheros WLAN card, the middle one +is the correct interface in this case. The interface name for -i +command line option is the full string following "ifname:" (the +"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant +would be started with the following command: + +# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d + +-d optional enables some more debugging (use -dd for even more, if +needed). It can be left out if debugging information is not needed. + +With the alternative mechanism for selecting the interface, this +command has identical results in this case: + +# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d + + +Simple configuration example for WPA-PSK: + +#ap_scan=2 +ctrl_interface= +network={ + ssid="test" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + psk="secret passphrase" +} + +(remove '#' from the comment out ap_scan line to enable mode in which +wpa_supplicant tries to associate with the SSID without doing +scanning; this allows APs with hidden SSIDs to be used) + + +wpa_cli.exe and wpa_gui.exe can be used to interact with the +wpa_supplicant.exe program in the same way as with Linux. Note that +ctrl_interface is using UNIX domain sockets when built for cygwin, but +the native build for Windows uses named pipes and the contents of the +ctrl_interface configuration item is used to control access to the +interface. Anyway, this variable has to be included in the configuration +to enable the control interface. + + +Example SDDL string formats: + +(local admins group has permission, but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA) + +("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and +"BA" == "builtin administrators" == the local admins. The empty fields +are for flags and object GUIDs, none of which should be required in this +case.) + +(local admins and the local "power users" group have permissions, +but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and +one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.) + +(close to wide open, but you have to be a valid user on +the machine): + +ctrl_interface=SDDL=D:(A;;GA;;;AU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users" +group.) + +This one would allow absolutely everyone (including anonymous +users) -- this is *not* recommended, since named pipes can be attached +to from anywhere on the network (i.e. there's no "this machine only" +like there is with 127.0.0.1 sockets): + +ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN) + +(BU == "builtin users", "AN" == "anonymous") + +See also [1] for the format of ACEs, and [2] for the possible strings +that can be used for principal names. + +[1] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp +[2] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp + + +Starting wpa_supplicant as a Windows service (wpasvc.exe) +--------------------------------------------------------- + +wpa_supplicant can be started as a Windows service by using wpasvc.exe +program that is alternative build of wpa_supplicant.exe. Most of the +core functionality of wpasvc.exe is identical to wpa_supplicant.exe, +but it is using Windows registry for configuration information instead +of a text file and command line parameters. In addition, it can be +registered as a service that can be started automatically or manually +like any other Windows service. + +The root of wpa_supplicant configuration in registry is +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global +parameters and a 'interfaces' subkey with all the interface configuration +(adapter to confname mapping). Each such mapping is a subkey that has +'adapter', 'config', and 'ctrl_interface' values. + +This program can be run either as a normal command line application, +e.g., for debugging, with 'wpasvc.exe app' or as a Windows service. +Service need to be registered with 'wpasvc.exe reg <full path to +wpasvc.exe>'. Alternatively, 'wpasvc.exe reg' can be used to register +the service with the current location of wpasvc.exe. After this, wpasvc +can be started like any other Windows service (e.g., 'net start wpasvc') +or it can be configured to start automatically through the Services tool +in administrative tasks. The service can be unregistered with +'wpasvc.exe unreg'. + +If the service is set to start during system bootup to make the +network connection available before any user has logged in, there may +be a long (half a minute or so) delay in starting up wpa_supplicant +due to WinPcap needing a driver called "Network Monitor Driver" which +is started by default on demand. + +To speed up wpa_supplicant start during system bootup, "Network +Monitor Driver" can be configured to be started sooner by setting its +startup type to System instead of the default Demand. To do this, open +up Device Manager, select Show Hidden Devices, expand the "Non +Plug-and-Play devices" branch, double click "Network Monitor Driver", +go to the Driver tab, and change the Demand setting to System instead. + +Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs +key. Each configuration profile has its own key under this. In terms of text +files, each profile would map to a separate text file with possibly multiple +networks. Under each profile, there is a networks key that lists all +networks as a subkey. Each network has set of values in the same way as +network block in the configuration file. In addition, blobs subkey has +possible blobs as values. + +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK + +See win_example.reg for an example on how to setup wpasvc.exe +parameters in registry. It can also be imported to registry as a +starting point for the configuration. + + + +License information for third party software used in this product: + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + + + Qt Open Source Edition + ---------------------- + +The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA. +Qt Open Source Edition is licensed under GPL version 2. + +Source code for the library is available at +http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c new file mode 100644 index 0000000000000..4ffb22097370a --- /dev/null +++ b/wpa_supplicant/blacklist.c @@ -0,0 +1,133 @@ +/* + * wpa_supplicant - Temporary BSSID blacklist + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "blacklist.h" + +/** + * wpa_blacklist_get - Get the blacklist entry for a BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Matching blacklist entry for the BSSID or %NULL if not found + */ +struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_s->blacklist; + while (e) { + if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) + return e; + e = e->next; + } + + return NULL; +} + + +/** + * wpa_blacklist_add - Add an BSSID to the blacklist + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to be added to the blacklist + * Returns: 0 on success, -1 on failure + * + * This function adds the specified BSSID to the blacklist or increases the + * blacklist count if the BSSID was already listed. It should be called when + * an association attempt fails either due to the selected BSS rejecting + * association or due to timeout. + * + * This blacklist is used to force %wpa_supplicant to go through all available + * BSSes before retrying to associate with an BSS that rejected or timed out + * association. It does not prevent the listed BSS from being used; it only + * changes the order in which they are tried. + */ +int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_blacklist_get(wpa_s, bssid); + if (e) { + e->count++; + wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count " + "incremented to %d", + MAC2STR(bssid), e->count); + return 0; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + os_memcpy(e->bssid, bssid, ETH_ALEN); + e->count = 1; + e->next = wpa_s->blacklist; + wpa_s->blacklist = e; + wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", + MAC2STR(bssid)); + + return 0; +} + + +/** + * wpa_blacklist_del - Remove an BSSID from the blacklist + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to be removed from the blacklist + * Returns: 0 on success, -1 on failure + */ +int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e, *prev = NULL; + + e = wpa_s->blacklist; + while (e) { + if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) { + if (prev == NULL) { + wpa_s->blacklist = e->next; + } else { + prev->next = e->next; + } + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist", MAC2STR(bssid)); + os_free(e); + return 0; + } + prev = e; + e = e->next; + } + return -1; +} + + +/** + * wpa_blacklist_clear - Clear the blacklist of all entries + * @wpa_s: Pointer to wpa_supplicant data + */ +void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) +{ + struct wpa_blacklist *e, *prev; + + e = wpa_s->blacklist; + wpa_s->blacklist = NULL; + while (e) { + prev = e; + e = e->next; + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist (clear)", MAC2STR(prev->bssid)); + os_free(prev); + } +} diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h new file mode 100644 index 0000000000000..de280cd71568c --- /dev/null +++ b/wpa_supplicant/blacklist.h @@ -0,0 +1,30 @@ +/* + * wpa_supplicant - Temporary BSSID blacklist + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BLACKLIST_H +#define BLACKLIST_H + +struct wpa_blacklist { + struct wpa_blacklist *next; + u8 bssid[ETH_ALEN]; + int count; +}; + +struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, + const u8 *bssid); +int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid); +void wpa_blacklist_clear(struct wpa_supplicant *wpa_s); + +#endif /* BLACKLIST_H */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c new file mode 100644 index 0000000000000..9a7937417df1b --- /dev/null +++ b/wpa_supplicant/config.c @@ -0,0 +1,1978 @@ +/* + * WPA Supplicant / Configuration parser and common functions + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "sha1.h" +#include "eap_peer/eap.h" +#include "config.h" + + +#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE) +#define NO_CONFIG_WRITE +#endif + +/* + * Structure for network configuration parsing. This data is used to implement + * a generic parser for each network block variable. The table of configuration + * variables is defined below in this file (ssid_fields[]). + */ +struct parse_data { + /* Configuration variable name */ + char *name; + + /* Parser function for this variable */ + int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid, + int line, const char *value); + +#ifndef NO_CONFIG_WRITE + /* Writer function (i.e., to get the variable in text format from + * internal presentation). */ + char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid); +#endif /* NO_CONFIG_WRITE */ + + /* Variable specific parameters for the parser. */ + void *param1, *param2, *param3, *param4; + + /* 0 = this variable can be included in debug output and ctrl_iface + * 1 = this variable contains key/private data and it must not be + * included in debug output unless explicitly requested. In + * addition, this variable will not be readable through the + * ctrl_iface. + */ + int key_data; +}; + + +static char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = os_malloc(*len + 1); + if (str == NULL) + return NULL; + os_memcpy(str, value, *len); + str[*len] = '\0'; + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +static int wpa_config_parse_str(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + size_t res_len, *dst_len; + char **dst, *tmp; + + if (os_strcmp(value, "NULL") == 0) { + wpa_printf(MSG_DEBUG, "Unset configuration string '%s'", + data->name); + tmp = NULL; + res_len = 0; + goto set; + } + + tmp = wpa_config_parse_string(value, &res_len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.", + line, data->name, + data->key_data ? "[KEY DATA REMOVED]" : value); + return -1; + } + + if (data->key_data) { + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + } else { + wpa_hexdump_ascii(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + } + + if (data->param3 && res_len < (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param3); + os_free(tmp); + return -1; + } + + if (data->param4 && res_len > (size_t) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param4); + os_free(tmp); + return -1; + } + +set: + dst = (char **) (((u8 *) ssid) + (long) data->param1); + dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2); + os_free(*dst); + *dst = tmp; + if (data->param2) + *dst_len = res_len; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +static char * wpa_config_write_string_ascii(const u8 *value, size_t len) +{ + char *buf; + + buf = os_malloc(len + 3); + if (buf == NULL) + return NULL; + buf[0] = '"'; + os_memcpy(buf + 1, value, len); + buf[len + 1] = '"'; + buf[len + 2] = '\0'; + + return buf; +} + + +static char * wpa_config_write_string_hex(const u8 *value, size_t len) +{ + char *buf; + + buf = os_zalloc(2 * len + 1); + if (buf == NULL) + return NULL; + wpa_snprintf_hex(buf, 2 * len + 1, value, len); + + return buf; +} + + +static char * wpa_config_write_string(const u8 *value, size_t len) +{ + if (value == NULL) + return NULL; + + if (is_hex(value, len)) + return wpa_config_write_string_hex(value, len); + else + return wpa_config_write_string_ascii(value, len); +} + + +static char * wpa_config_write_str(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + size_t len; + char **src; + + src = (char **) (((u8 *) ssid) + (long) data->param1); + if (*src == NULL) + return NULL; + + if (data->param2) + len = *((size_t *) (((u8 *) ssid) + (long) data->param2)); + else + len = os_strlen(*src); + + return wpa_config_write_string((const u8 *) *src, len); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_int(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + int *dst; + + dst = (int *) (((u8 *) ssid) + (long) data->param1); + *dst = atoi(value); + wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); + + if (data->param3 && *dst < (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + if (data->param4 && *dst > (long) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param4); + *dst = (long) data->param4; + return -1; + } + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_int(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int *src, res; + char *value; + + src = (int *) (((u8 *) ssid) + (long) data->param1); + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, "%d", *src); + if (res < 0 || res >= 20) { + os_free(value); + return NULL; + } + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_bssid(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (hwaddr_aton(value, ssid->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", + line, value); + return -1; + } + ssid->bssid_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bssid(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + int res; + + if (!ssid->bssid_set) + return NULL; + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid)); + if (res < 0 || res >= 20) { + os_free(value); + return NULL; + } + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_psk(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (*value == '"') { +#ifndef CONFIG_NO_PBKDF2 + const char *pos; + size_t len; + + value++; + pos = os_strrchr(value, '"'); + if (pos) + len = pos - value; + else + len = os_strlen(value); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase " + "length %lu (expected: 8..63) '%s'.", + line, (unsigned long) len, value); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", + (u8 *) value, len); + if (ssid->passphrase && os_strlen(ssid->passphrase) == len && + os_memcmp(ssid->passphrase, value, len) == 0) + return 0; + ssid->psk_set = 0; + os_free(ssid->passphrase); + ssid->passphrase = os_malloc(len + 1); + if (ssid->passphrase == NULL) + return -1; + os_memcpy(ssid->passphrase, value, len); + ssid->passphrase[len] = '\0'; + return 0; +#else /* CONFIG_NO_PBKDF2 */ + wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not " + "supported.", line); + return -1; +#endif /* CONFIG_NO_PBKDF2 */ + } + + if (hexstr2bin(value, ssid->psk, PMK_LEN) || + value[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, value); + return -1; + } + + os_free(ssid->passphrase); + ssid->passphrase = NULL; + + ssid->psk_set = 1; + wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_psk(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + if (ssid->passphrase) + return wpa_config_write_string_ascii( + (const u8 *) ssid->passphrase, + os_strlen(ssid->passphrase)); + + if (ssid->psk_set) + return wpa_config_write_string_hex(ssid->psk, PMK_LEN); + + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_proto(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA") == 0) + val |= WPA_PROTO_WPA; + else if (os_strcmp(start, "RSN") == 0 || + os_strcmp(start, "WPA2") == 0) + val |= WPA_PROTO_RSN; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no proto values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); + ssid->proto = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_proto(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1, ret; + char *buf, *pos, *end; + + pos = buf = os_zalloc(10); + if (buf == NULL) + return NULL; + end = buf + 10; + + if (ssid->proto & WPA_PROTO_WPA) { + ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return buf; + pos += ret; + first = 0; + } + + if (ssid->proto & WPA_PROTO_RSN) { + ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return buf; + pos += ret; + first = 0; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_key_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else if (os_strcmp(start, "IEEE8021X") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_KEY_MGMT_NONE; + else if (os_strcmp(start, "WPA-NONE") == 0) + val |= WPA_KEY_MGMT_WPA_NONE; +#ifdef CONFIG_IEEE80211R + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) + val |= WPA_KEY_MGMT_PSK_SHA256; + else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SHA256; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + else if (os_strcmp(start, "WPS") == 0) + val |= WPA_KEY_MGMT_WPS; +#endif /* CONFIG_WPS */ + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no key_mgmt values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); + ssid->key_mgmt = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_key_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(50); + if (buf == NULL) + return NULL; + end = buf + 50; + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "%sWPA-PSK", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + ret = os_snprintf(pos, end - pos, "%sIEEE8021X", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, "%sWPA-NONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + +#ifdef CONFIG_IEEE80211R + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) + pos += os_snprintf(pos, end - pos, "%sFT-PSK", + pos == buf ? "" : " "); + + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + pos += os_snprintf(pos, end - pos, "%sFT-EAP", + pos == buf ? "" : " "); +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", + pos == buf ? "" : " "); + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", + pos == buf ? "" : " "); +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + pos += os_snprintf(pos, end - pos, "%sWPS", + pos == buf ? "" : " "); +#endif /* CONFIG_WPS */ + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_cipher(int cipher) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(50); + if (buf == NULL) + return NULL; + end = buf + 50; + + if (cipher & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); + ssid->pairwise_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->pairwise_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_group(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | + WPA_CIPHER_WEP40)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); + ssid->group_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_group(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->group_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_auth_alg(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "OPEN") == 0) + val |= WPA_AUTH_ALG_OPEN; + else if (os_strcmp(start, "SHARED") == 0) + val |= WPA_AUTH_ALG_SHARED; + else if (os_strcmp(start, "LEAP") == 0) + val |= WPA_AUTH_ALG_LEAP; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no auth_alg values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); + ssid->auth_alg = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_auth_alg(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(30); + if (buf == NULL) + return NULL; + end = buf + 30; + + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) { + ret = os_snprintf(pos, end - pos, "%sOPEN", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) { + ret = os_snprintf(pos, end - pos, "%sSHARED", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) { + ret = os_snprintf(pos, end - pos, "%sLEAP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +#ifdef IEEE8021X_EAPOL +static int wpa_config_parse_eap(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int last, errors = 0; + char *start, *end, *buf; + struct eap_method_type *methods = NULL, *tmp; + size_t num_methods = 0; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + tmp = methods; + methods = os_realloc(methods, + (num_methods + 1) * sizeof(*methods)); + if (methods == NULL) { + os_free(tmp); + os_free(buf); + return -1; + } + methods[num_methods].method = eap_peer_get_type( + start, &methods[num_methods].vendor); + if (methods[num_methods].vendor == EAP_VENDOR_IETF && + methods[num_methods].method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP method " + "'%s'", line, start); + wpa_printf(MSG_ERROR, "You may need to add support for" + " this EAP method during wpa_supplicant\n" + "build time configuration.\n" + "See README for more information."); + errors++; + } else if (methods[num_methods].vendor == EAP_VENDOR_IETF && + methods[num_methods].method == EAP_TYPE_LEAP) + ssid->leap++; + else + ssid->non_leap++; + num_methods++; + if (last) + break; + start = end + 1; + } + os_free(buf); + + tmp = methods; + methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods)); + if (methods == NULL) { + os_free(tmp); + return -1; + } + methods[num_methods].vendor = EAP_VENDOR_IETF; + methods[num_methods].method = EAP_TYPE_NONE; + num_methods++; + + wpa_hexdump(MSG_MSGDUMP, "eap methods", + (u8 *) methods, num_methods * sizeof(*methods)); + ssid->eap.eap_methods = methods; + return errors ? -1 : 0; +} + + +static char * wpa_config_write_eap(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int i, ret; + char *buf, *pos, *end; + const struct eap_method_type *eap_methods = ssid->eap.eap_methods; + const char *name; + + if (eap_methods == NULL) + return NULL; + + pos = buf = os_zalloc(100); + if (buf == NULL) + return NULL; + end = buf + 100; + + for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF || + eap_methods[i].method != EAP_TYPE_NONE; i++) { + name = eap_get_name(eap_methods[i].vendor, + eap_methods[i].method); + if (name) { + ret = os_snprintf(pos, end - pos, "%s%s", + pos == buf ? "" : " ", name); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + } + + end[-1] = '\0'; + + return buf; +} + + +static int wpa_config_parse_password(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + u8 *hash; + + if (os_strcmp(value, "NULL") == 0) { + wpa_printf(MSG_DEBUG, "Unset configuration string 'password'"); + os_free(ssid->eap.password); + ssid->eap.password = NULL; + ssid->eap.password_len = 0; + return 0; + } + + if (os_strncmp(value, "hash:", 5) != 0) { + char *tmp; + size_t res_len; + + tmp = wpa_config_parse_string(value, &res_len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse " + "password.", line); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + + os_free(ssid->eap.password); + ssid->eap.password = (u8 *) tmp; + ssid->eap.password_len = res_len; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + + return 0; + } + + + /* NtPasswordHash: hash:<32 hex digits> */ + if (os_strlen(value + 5) != 2 * 16) { + wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length " + "(expected 32 hex digits)", line); + return -1; + } + + hash = os_malloc(16); + if (hash == NULL) + return -1; + + if (hexstr2bin(value + 5, hash, 16)) { + os_free(hash); + wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); + + os_free(ssid->eap.password); + ssid->eap.password = hash; + ssid->eap.password_len = 16; + ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + + return 0; +} + + +static char * wpa_config_write_password(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf; + + if (ssid->eap.password == NULL) + return NULL; + + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { + return wpa_config_write_string( + ssid->eap.password, ssid->eap.password_len); + } + + buf = os_malloc(5 + 32 + 1); + if (buf == NULL) + return NULL; + + os_memcpy(buf, "hash:", 5); + wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16); + + return buf; +} +#endif /* IEEE8021X_EAPOL */ + + +static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, + const char *value, int idx) +{ + char *buf, title[20]; + int res; + + buf = wpa_config_parse_string(value, len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.", + line, idx, value); + return -1; + } + if (*len > MAX_WEP_KEY_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.", + line, idx, value); + os_free(buf); + return -1; + } + os_memcpy(key, buf, *len); + os_free(buf); + res = os_snprintf(title, sizeof(title), "wep_key%d", idx); + if (res >= 0 && (size_t) res < sizeof(title)) + wpa_hexdump_key(MSG_MSGDUMP, title, key, *len); + return 0; +} + + +static int wpa_config_parse_wep_key0(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[0], + &ssid->wep_key_len[0], line, + value, 0); +} + + +static int wpa_config_parse_wep_key1(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[1], + &ssid->wep_key_len[1], line, + value, 1); +} + + +static int wpa_config_parse_wep_key2(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[2], + &ssid->wep_key_len[2], line, + value, 2); +} + + +static int wpa_config_parse_wep_key3(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[3], + &ssid->wep_key_len[3], line, + value, 3); +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx) +{ + if (ssid->wep_key_len[idx] == 0) + return NULL; + return wpa_config_write_string(ssid->wep_key[idx], + ssid->wep_key_len[idx]); +} + + +static char * wpa_config_write_wep_key0(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 0); +} + + +static char * wpa_config_write_wep_key1(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 1); +} + + +static char * wpa_config_write_wep_key2(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 2); +} + + +static char * wpa_config_write_wep_key3(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 3); +} +#endif /* NO_CONFIG_WRITE */ + + +/* Helper macros for network block parser */ + +#ifdef OFFSET +#undef OFFSET +#endif /* OFFSET */ +/* OFFSET: Get offset of a variable within the wpa_ssid structure */ +#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v) + +/* STR: Define a string variable for an ASCII string; f = field name */ +#ifdef NO_CONFIG_WRITE +#define _STR(f) #f, wpa_config_parse_str, OFFSET(f) +#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f) +#else /* NO_CONFIG_WRITE */ +#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f) +#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f) +#endif /* NO_CONFIG_WRITE */ +#define STR(f) _STR(f), NULL, NULL, NULL, 0 +#define STRe(f) _STRe(f), NULL, NULL, NULL, 0 +#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1 +#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1 + +/* STR_LEN: Define a string variable with a separate variable for storing the + * data length. Unlike STR(), this can be used to store arbitrary binary data + * (i.e., even nul termination character). */ +#define _STR_LEN(f) _STR(f), OFFSET(f ## _len) +#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len) +#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0 +#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0 +#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1 + +/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length + * explicitly specified. */ +#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max) +#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0 +#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1 + +#ifdef NO_CONFIG_WRITE +#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0 +#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0 +#else /* NO_CONFIG_WRITE */ +#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \ + OFFSET(f), (void *) 0 +#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \ + OFFSET(eap.f), (void *) 0 +#endif /* NO_CONFIG_WRITE */ + +/* INT: Define an integer variable */ +#define INT(f) _INT(f), NULL, NULL, 0 +#define INTe(f) _INTe(f), NULL, NULL, 0 + +/* INT_RANGE: Define an integer variable with allowed value range */ +#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0 + +/* FUNC: Define a configuration variable that uses a custom function for + * parsing and writing the value. */ +#ifdef NO_CONFIG_WRITE +#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL +#else /* NO_CONFIG_WRITE */ +#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \ + NULL, NULL, NULL, NULL +#endif /* NO_CONFIG_WRITE */ +#define FUNC(f) _FUNC(f), 0 +#define FUNC_KEY(f) _FUNC(f), 1 + +/* + * Table of network configuration variables. This table is used to parse each + * network configuration variable, e.g., each line in wpa_supplicant.conf file + * that is inside a network block. + * + * This table is generated using the helper macros defined above and with + * generous help from the C pre-processor. The field name is stored as a string + * into .name and for STR and INT types, the offset of the target buffer within + * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar + * offset to the field containing the length of the configuration variable. + * .param3 and .param4 can be used to mark the allowed range (length for STR + * and value for INT). + * + * For each configuration line in wpa_supplicant.conf, the parser goes through + * this table and select the entry that matches with the field name. The parser + * function (.parser) is then called to parse the actual value of the field. + * + * This kind of mechanism makes it easy to add new configuration parameters, + * since only one line needs to be added into this table and into the + * struct wpa_ssid definition if the new variable is either a string or + * integer. More complex types will need to use their own parser and writer + * functions. + */ +static const struct parse_data ssid_fields[] = { + { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { INT_RANGE(scan_ssid, 0, 1) }, + { FUNC(bssid) }, + { FUNC_KEY(psk) }, + { FUNC(proto) }, + { FUNC(key_mgmt) }, + { FUNC(pairwise) }, + { FUNC(group) }, + { FUNC(auth_alg) }, +#ifdef IEEE8021X_EAPOL + { FUNC(eap) }, + { STR_LENe(identity) }, + { STR_LENe(anonymous_identity) }, + { FUNC_KEY(password) }, + { STRe(ca_cert) }, + { STRe(ca_path) }, + { STRe(client_cert) }, + { STRe(private_key) }, + { STR_KEYe(private_key_passwd) }, + { STRe(dh_file) }, + { STRe(subject_match) }, + { STRe(altsubject_match) }, + { STRe(ca_cert2) }, + { STRe(ca_path2) }, + { STRe(client_cert2) }, + { STRe(private_key2) }, + { STR_KEYe(private_key2_passwd) }, + { STRe(dh_file2) }, + { STRe(subject_match2) }, + { STRe(altsubject_match2) }, + { STRe(phase1) }, + { STRe(phase2) }, + { STRe(pcsc) }, + { STR_KEYe(pin) }, + { STRe(engine_id) }, + { STRe(key_id) }, + { STRe(cert_id) }, + { STRe(ca_cert_id) }, + { STR_KEYe(pin2) }, + { STRe(engine2_id) }, + { STRe(key2_id) }, + { STRe(cert2_id) }, + { STRe(ca_cert2_id) }, + { INTe(engine) }, + { INTe(engine2) }, + { INT(eapol_flags) }, +#endif /* IEEE8021X_EAPOL */ + { FUNC_KEY(wep_key0) }, + { FUNC_KEY(wep_key1) }, + { FUNC_KEY(wep_key2) }, + { FUNC_KEY(wep_key3) }, + { INT(wep_tx_keyidx) }, + { INT(priority) }, +#ifdef IEEE8021X_EAPOL + { INT(eap_workaround) }, + { STRe(pac_file) }, + { INTe(fragment_size) }, +#endif /* IEEE8021X_EAPOL */ + { INT_RANGE(mode, 0, 1) }, + { INT_RANGE(proactive_key_caching, 0, 1) }, + { INT_RANGE(disabled, 0, 1) }, + { STR(id_str) }, +#ifdef CONFIG_IEEE80211W + { INT_RANGE(ieee80211w, 0, 2) }, +#endif /* CONFIG_IEEE80211W */ + { INT_RANGE(peerkey, 0, 1) }, + { INT_RANGE(mixed_cell, 0, 1) }, + { INT_RANGE(frequency, 0, 10000) }, + { INT(wpa_ptk_rekey) } +}; + +#undef OFFSET +#undef _STR +#undef STR +#undef STR_KEY +#undef _STR_LEN +#undef STR_LEN +#undef STR_LEN_KEY +#undef _STR_RANGE +#undef STR_RANGE +#undef STR_RANGE_KEY +#undef _INT +#undef INT +#undef INT_RANGE +#undef _FUNC +#undef FUNC +#undef FUNC_KEY +#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) + + +/** + * wpa_config_add_prio_network - Add a network to priority lists + * @config: Configuration data from wpa_config_read() + * @ssid: Pointer to the network configuration to be added to the list + * Returns: 0 on success, -1 on failure + * + * This function is used to add a network block to the priority list of + * networks. This must be called for each network when reading in the full + * configuration. In addition, this can be used indirectly when updating + * priorities by calling wpa_config_update_prio_list(). + */ +int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid) +{ + int prio; + struct wpa_ssid *prev, **nlist; + + /* + * Add to an existing priority list if one is available for the + * configured priority level for this network. + */ + for (prio = 0; prio < config->num_prio; prio++) { + prev = config->pssid[prio]; + if (prev->priority == ssid->priority) { + while (prev->pnext) + prev = prev->pnext; + prev->pnext = ssid; + return 0; + } + } + + /* First network for this priority - add a new priority list */ + nlist = os_realloc(config->pssid, + (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + if (nlist == NULL) + return -1; + + for (prio = 0; prio < config->num_prio; prio++) { + if (nlist[prio]->priority < ssid->priority) + break; + } + + os_memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * sizeof(struct wpa_ssid *)); + + nlist[prio] = ssid; + config->num_prio++; + config->pssid = nlist; + + return 0; +} + + +/** + * wpa_config_update_prio_list - Update network priority list + * @config: Configuration data from wpa_config_read() + * Returns: 0 on success, -1 on failure + * + * This function is called to update the priority list of networks in the + * configuration when a network is being added or removed. This is also called + * if a priority for a network is changed. + */ +static int wpa_config_update_prio_list(struct wpa_config *config) +{ + struct wpa_ssid *ssid; + int ret = 0; + + os_free(config->pssid); + config->pssid = NULL; + config->num_prio = 0; + + ssid = config->ssid; + while (ssid) { + ssid->pnext = NULL; + if (wpa_config_add_prio_network(config, ssid) < 0) + ret = -1; + ssid = ssid->next; + } + + return ret; +} + + +#ifdef IEEE8021X_EAPOL +static void eap_peer_config_free(struct eap_peer_config *eap) +{ + os_free(eap->eap_methods); + os_free(eap->identity); + os_free(eap->anonymous_identity); + os_free(eap->password); + os_free(eap->ca_cert); + os_free(eap->ca_path); + os_free(eap->client_cert); + os_free(eap->private_key); + os_free(eap->private_key_passwd); + os_free(eap->dh_file); + os_free(eap->subject_match); + os_free(eap->altsubject_match); + os_free(eap->ca_cert2); + os_free(eap->ca_path2); + os_free(eap->client_cert2); + os_free(eap->private_key2); + os_free(eap->private_key2_passwd); + os_free(eap->dh_file2); + os_free(eap->subject_match2); + os_free(eap->altsubject_match2); + os_free(eap->phase1); + os_free(eap->phase2); + os_free(eap->pcsc); + os_free(eap->pin); + os_free(eap->engine_id); + os_free(eap->key_id); + os_free(eap->cert_id); + os_free(eap->ca_cert_id); + os_free(eap->key2_id); + os_free(eap->cert2_id); + os_free(eap->ca_cert2_id); + os_free(eap->pin2); + os_free(eap->engine2_id); + os_free(eap->otp); + os_free(eap->pending_req_otp); + os_free(eap->pac_file); + os_free(eap->new_password); +} +#endif /* IEEE8021X_EAPOL */ + + +/** + * wpa_config_free_ssid - Free network/ssid configuration data + * @ssid: Configuration data for the network + * + * This function frees all resources allocated for the network configuration + * data. + */ +void wpa_config_free_ssid(struct wpa_ssid *ssid) +{ + os_free(ssid->ssid); + os_free(ssid->passphrase); +#ifdef IEEE8021X_EAPOL + eap_peer_config_free(&ssid->eap); +#endif /* IEEE8021X_EAPOL */ + os_free(ssid->id_str); + os_free(ssid); +} + + +/** + * wpa_config_free - Free configuration data + * @config: Configuration data from wpa_config_read() + * + * This function frees all resources allocated for the configuration data by + * wpa_config_read(). + */ +void wpa_config_free(struct wpa_config *config) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct wpa_config_blob *blob, *prevblob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ + struct wpa_ssid *ssid, *prev = NULL; + ssid = config->ssid; + while (ssid) { + prev = ssid; + ssid = ssid->next; + wpa_config_free_ssid(prev); + } + +#ifndef CONFIG_NO_CONFIG_BLOBS + blob = config->blobs; + prevblob = NULL; + while (blob) { + prevblob = blob; + blob = blob->next; + wpa_config_free_blob(prevblob); + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + os_free(config->ctrl_interface); + os_free(config->ctrl_interface_group); +#ifdef EAP_TLS_OPENSSL + os_free(config->opensc_engine_path); + os_free(config->pkcs11_engine_path); + os_free(config->pkcs11_module_path); +#endif /* EAP_TLS_OPENSSL */ + os_free(config->driver_param); + os_free(config->device_name); + os_free(config->manufacturer); + os_free(config->model_name); + os_free(config->model_number); + os_free(config->serial_number); + os_free(config->device_type); + os_free(config->pssid); + os_free(config); +} + + +/** + * wpa_config_get_network - Get configured network based on id + * @config: Configuration data from wpa_config_read() + * @id: Unique network id to search for + * Returns: Network configuration or %NULL if not found + */ +struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id) +{ + struct wpa_ssid *ssid; + + ssid = config->ssid; + while (ssid) { + if (id == ssid->id) + break; + ssid = ssid->next; + } + + return ssid; +} + + +/** + * wpa_config_add_network - Add a new network with empty configuration + * @config: Configuration data from wpa_config_read() + * Returns: The new network configuration or %NULL if operation failed + */ +struct wpa_ssid * wpa_config_add_network(struct wpa_config *config) +{ + int id; + struct wpa_ssid *ssid, *last = NULL; + + id = -1; + ssid = config->ssid; + while (ssid) { + if (ssid->id > id) + id = ssid->id; + last = ssid; + ssid = ssid->next; + } + id++; + + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + ssid->id = id; + if (last) + last->next = ssid; + else + config->ssid = ssid; + + wpa_config_update_prio_list(config); + + return ssid; +} + + +/** + * wpa_config_remove_network - Remove a configured network based on id + * @config: Configuration data from wpa_config_read() + * @id: Unique network id to search for + * Returns: 0 on success, or -1 if the network was not found + */ +int wpa_config_remove_network(struct wpa_config *config, int id) +{ + struct wpa_ssid *ssid, *prev = NULL; + + ssid = config->ssid; + while (ssid) { + if (id == ssid->id) + break; + prev = ssid; + ssid = ssid->next; + } + + if (ssid == NULL) + return -1; + + if (prev) + prev->next = ssid->next; + else + config->ssid = ssid->next; + + wpa_config_update_prio_list(config); + wpa_config_free_ssid(ssid); + return 0; +} + + +/** + * wpa_config_set_network_defaults - Set network default values + * @ssid: Pointer to network configuration data + */ +void wpa_config_set_network_defaults(struct wpa_ssid *ssid) +{ + ssid->proto = DEFAULT_PROTO; + ssid->pairwise_cipher = DEFAULT_PAIRWISE; + ssid->group_cipher = DEFAULT_GROUP; + ssid->key_mgmt = DEFAULT_KEY_MGMT; +#ifdef IEEE8021X_EAPOL + ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; + ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; + ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; +#endif /* IEEE8021X_EAPOL */ +} + + +/** + * wpa_config_set - Set a variable in network configuration + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * @value: Variable value + * @line: Line number in configuration file or 0 if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to set network configuration variables based on + * both the configuration file and management interface input. The value + * parameter must be in the same format as the text-based configuration file is + * using. For example, strings are using double quotation marks. + */ +int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, + int line) +{ + size_t i; + int ret = 0; + + if (ssid == NULL || var == NULL || value == NULL) + return -1; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) != 0) + continue; + + if (field->parser(field, ssid, line, value)) { + if (line) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse %s '%s'.", line, var, value); + } + ret = -1; + } + break; + } + if (i == NUM_SSID_FIELDS) { + if (line) { + wpa_printf(MSG_ERROR, "Line %d: unknown network field " + "'%s'.", line, var); + } + ret = -1; + } + + return ret; +} + + +#ifndef NO_CONFIG_WRITE +/** + * wpa_config_get - Get a variable in network configuration + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * Returns: Value of the variable or %NULL on failure + * + * This function can be used to get network configuration variables. The + * returned value is a copy of the configuration variable in text format, i.e,. + * the same format that the text-based configuration file and wpa_config_set() + * are using for the value. The caller is responsible for freeing the returned + * value. + */ +char * wpa_config_get(struct wpa_ssid *ssid, const char *var) +{ + size_t i; + + if (ssid == NULL || var == NULL) + return NULL; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) == 0) + return field->writer(field, ssid); + } + + return NULL; +} + + +/** + * wpa_config_get_no_key - Get a variable in network configuration (no keys) + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * Returns: Value of the variable or %NULL on failure + * + * This function can be used to get network configuration variable like + * wpa_config_get(). The only difference is that this functions does not expose + * key/password material from the configuration. In case a key/password field + * is requested, the returned value is an empty string or %NULL if the variable + * is not set or "*" if the variable is set (regardless of its value). The + * returned value is a copy of the configuration variable in text format, i.e,. + * the same format that the text-based configuration file and wpa_config_set() + * are using for the value. The caller is responsible for freeing the returned + * value. + */ +char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var) +{ + size_t i; + + if (ssid == NULL || var == NULL) + return NULL; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) == 0) { + char *res = field->writer(field, ssid); + if (field->key_data) { + if (res && res[0]) { + wpa_printf(MSG_DEBUG, "Do not allow " + "key_data field to be " + "exposed"); + os_free(res); + return os_strdup("*"); + } + + os_free(res); + return NULL; + } + return res; + } + } + + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + +/** + * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID + * @ssid: Pointer to network configuration data + * + * This function must be called to update WPA PSK when either SSID or the + * passphrase has changed for the network configuration. + */ +void wpa_config_update_psk(struct wpa_ssid *ssid) +{ +#ifndef CONFIG_NO_PBKDF2 + pbkdf2_sha1(ssid->passphrase, + (char *) ssid->ssid, ssid->ssid_len, 4096, + ssid->psk, PMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", + ssid->psk, PMK_LEN); + ssid->psk_set = 1; +#endif /* CONFIG_NO_PBKDF2 */ +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +/** + * wpa_config_get_blob - Get a named configuration blob + * @config: Configuration data from wpa_config_read() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config, + const char *name) +{ + struct wpa_config_blob *blob = config->blobs; + + while (blob) { + if (os_strcmp(blob->name, name) == 0) + return blob; + blob = blob->next; + } + return NULL; +} + + +/** + * wpa_config_set_blob - Set or add a named configuration blob + * @config: Configuration data from wpa_config_read() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void wpa_config_set_blob(struct wpa_config *config, + struct wpa_config_blob *blob) +{ + wpa_config_remove_blob(config, blob->name); + blob->next = config->blobs; + config->blobs = blob; +} + + +/** + * wpa_config_free_blob - Free blob data + * @blob: Pointer to blob to be freed + */ +void wpa_config_free_blob(struct wpa_config_blob *blob) +{ + if (blob) { + os_free(blob->name); + os_free(blob->data); + os_free(blob); + } +} + + +/** + * wpa_config_remove_blob - Remove a named configuration blob + * @config: Configuration data from wpa_config_read() + * @name: Name of the blob to remove + * Returns: 0 if blob was removed or -1 if blob was not found + */ +int wpa_config_remove_blob(struct wpa_config *config, const char *name) +{ + struct wpa_config_blob *pos = config->blobs, *prev = NULL; + + while (pos) { + if (os_strcmp(pos->name, name) == 0) { + if (prev) + prev->next = pos->next; + else + config->blobs = pos->next; + wpa_config_free_blob(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +/** + * wpa_config_alloc_empty - Allocate an empty configuration + * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain + * socket + * @driver_param: Driver parameters + * Returns: Pointer to allocated configuration data or %NULL on failure + */ +struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, + const char *driver_param) +{ + struct wpa_config *config; + + config = os_zalloc(sizeof(*config)); + if (config == NULL) + return NULL; + config->eapol_version = DEFAULT_EAPOL_VERSION; + config->ap_scan = DEFAULT_AP_SCAN; + config->fast_reauth = DEFAULT_FAST_REAUTH; + + if (ctrl_interface) + config->ctrl_interface = os_strdup(ctrl_interface); + if (driver_param) + config->driver_param = os_strdup(driver_param); + + return config; +} + + +#ifndef CONFIG_NO_STDOUT_DEBUG +/** + * wpa_config_debug_dump_networks - Debug dump of configured networks + * @config: Configuration data from wpa_config_read() + */ +void wpa_config_debug_dump_networks(struct wpa_config *config) +{ + int prio; + struct wpa_ssid *ssid; + + for (prio = 0; prio < config->num_prio; prio++) { + ssid = config->pssid[prio]; + wpa_printf(MSG_DEBUG, "Priority group %d", + ssid->priority); + while (ssid) { + wpa_printf(MSG_DEBUG, " id=%d ssid='%s'", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ssid = ssid->pnext; + } + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h new file mode 100644 index 0000000000000..4484e91d92152 --- /dev/null +++ b/wpa_supplicant/config.h @@ -0,0 +1,392 @@ +/* + * WPA Supplicant / Configuration file structures + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define DEFAULT_EAPOL_VERSION 1 +#ifdef CONFIG_NO_SCAN_PROCESSING +#define DEFAULT_AP_SCAN 2 +#else /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_AP_SCAN 1 +#endif /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_FAST_REAUTH 1 + +#include "config_ssid.h" + + +/** + * struct wpa_config - wpa_supplicant configuration data + * + * This data structure is presents the per-interface (radio) configuration + * data. In many cases, there is only one struct wpa_config instance, but if + * more than one network interface is being controlled, one instance is used + * for each. + */ +struct wpa_config { + /** + * ssid - Head of the global network list + * + * This is the head for the list of all the configured networks. + */ + struct wpa_ssid *ssid; + + /** + * pssid - Per-priority network lists (in priority order) + */ + struct wpa_ssid **pssid; + + /** + * num_prio - Number of different priorities used in the pssid lists + * + * This indicates how many per-priority network lists are included in + * pssid. + */ + int num_prio; + + /** + * eapol_version - IEEE 802.1X/EAPOL version number + * + * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which + * defines EAPOL version 2. However, there are many APs that do not + * handle the new version number correctly (they seem to drop the + * frames completely). In order to make wpa_supplicant interoperate + * with these APs, the version number is set to 1 by default. This + * configuration value can be used to set it to the new version (2). + */ + int eapol_version; + + /** + * ap_scan - AP scanning/selection + * + * By default, wpa_supplicant requests driver to perform AP + * scanning and then uses the scan results to select a + * suitable AP. Another alternative is to allow the driver to + * take care of AP scanning and selection and use + * wpa_supplicant just to process EAPOL frames based on IEEE + * 802.11 association information from the driver. + * + * 1: wpa_supplicant initiates scanning and AP selection (default). + * + * 0: Driver takes care of scanning, AP selection, and IEEE 802.11 + * association parameters (e.g., WPA IE generation); this mode can + * also be used with non-WPA drivers when using IEEE 802.1X mode; + * do not try to associate with APs (i.e., external program needs + * to control association). This mode must also be used when using + * wired Ethernet drivers. + * + * 2: like 0, but associate with APs using security policy and SSID + * (but not BSSID); this can be used, e.g., with ndiswrapper and NDIS + * drivers to enable operation with hidden SSIDs and optimized roaming; + * in this mode, the network blocks in the configuration are tried + * one by one until the driver reports successful association; each + * network block should have explicit security policy (i.e., only one + * option in the lists) for key_mgmt, pairwise, group, proto variables. + */ + int ap_scan; + + /** + * ctrl_interface - Parameters for the control interface + * + * If this is specified, %wpa_supplicant will open a control interface + * that is available for external programs to manage %wpa_supplicant. + * The meaning of this string depends on which control interface + * mechanism is used. For all cases, the existance of this parameter + * in configuration is used to determine whether the control interface + * is enabled. + * + * For UNIX domain sockets (default on Linux and BSD): This is a + * directory that will be created for UNIX domain sockets for listening + * to requests from external programs (CLI/GUI, etc.) for status + * information and configuration. The socket file will be named based + * on the interface name, so multiple %wpa_supplicant processes can be + * run at the same time if more than one interface is used. + * /var/run/wpa_supplicant is the recommended directory for sockets and + * by default, wpa_cli will use it when trying to connect with + * %wpa_supplicant. + * + * Access control for the control interface can be configured + * by setting the directory to allow only members of a group + * to use sockets. This way, it is possible to run + * %wpa_supplicant as root (since it needs to change network + * configuration and open raw sockets) and still allow GUI/CLI + * components to be run as non-root users. However, since the + * control interface can be used to change the network + * configuration, this access needs to be protected in many + * cases. By default, %wpa_supplicant is configured to use gid + * 0 (root). If you want to allow non-root users to use the + * control interface, add a new group and change this value to + * match with that group. Add users that should have control + * interface access to this group. + * + * When configuring both the directory and group, use following format: + * DIR=/var/run/wpa_supplicant GROUP=wheel + * DIR=/var/run/wpa_supplicant GROUP=0 + * (group can be either group name or gid) + * + * For UDP connections (default on Windows): The value will be ignored. + * This variable is just used to select that the control interface is + * to be created. The value can be set to, e.g., udp + * (ctrl_interface=udp). + * + * For Windows Named Pipe: This value can be used to set the security + * descriptor for controlling access to the control interface. Security + * descriptor can be set using Security Descriptor String Format (see + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp). + * The descriptor string needs to be prefixed with SDDL=. For example, + * ctrl_interface=SDDL=D: would set an empty DACL (which will reject + * all connections). + */ + char *ctrl_interface; + + /** + * ctrl_interface_group - Control interface group (DEPRECATED) + * + * This variable is only used for backwards compatibility. Group for + * UNIX domain sockets should now be specified using GROUP=group in + * ctrl_interface variable. + */ + char *ctrl_interface_group; + + /** + * fast_reauth - EAP fast re-authentication (session resumption) + * + * By default, fast re-authentication is enabled for all EAP methods + * that support it. This variable can be used to disable fast + * re-authentication (by setting fast_reauth=0). Normally, there is no + * need to disable fast re-authentication. + */ + int fast_reauth; + +#ifdef EAP_TLS_OPENSSL + /** + * 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; +#endif /* EAP_TLS_OPENSSL */ + + /** + * driver_param - Driver interface parameters + * + * This text string is passed to the selected driver interface with the + * optional struct wpa_driver_ops::set_param() handler. This can be + * used to configure driver specific options without having to add new + * driver interface functionality. + */ + char *driver_param; + + /** + * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK + * + * dot11 MIB variable for the maximum lifetime of a PMK in the PMK + * cache (unit: seconds). + */ + unsigned int dot11RSNAConfigPMKLifetime; + + /** + * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold + * + * dot11 MIB variable for the percentage of the PMK lifetime + * that should expire before an IEEE 802.1X reauthentication occurs. + */ + unsigned int dot11RSNAConfigPMKReauthThreshold; + + /** + * dot11RSNAConfigSATimeout - Security association timeout + * + * dot11 MIB variable for the maximum time a security association + * shall take to set up (unit: seconds). + */ + unsigned int dot11RSNAConfigSATimeout; + + /** + * update_config - Is wpa_supplicant allowed to update configuration + * + * This variable control whether wpa_supplicant is allow to re-write + * its configuration with wpa_config_write(). If this is zero, + * configuration data is only changed in memory and the external data + * is not overriden. If this is non-zero, wpa_supplicant will update + * the configuration data (e.g., a file) whenever configuration is + * changed. This update may replace the old configuration which can + * remove comments from it in case of a text file configuration. + */ + int update_config; + + /** + * blobs - Configuration blobs + */ + struct wpa_config_blob *blobs; + + /** + * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS + */ + u8 uuid[16]; + + /** + * device_name - Device Name (WPS) + * User-friendly description of device; up to 32 octets encoded in + * UTF-8 + */ + char *device_name; + + /** + * manufacturer - Manufacturer (WPS) + * The manufacturer of the device (up to 64 ASCII characters) + */ + char *manufacturer; + + /** + * model_name - Model Name (WPS) + * Model of the device (up to 32 ASCII characters) + */ + char *model_name; + + /** + * model_number - Model Number (WPS) + * Additional device description (up to 32 ASCII characters) + */ + char *model_number; + + /** + * serial_number - Serial Number (WPS) + * Serial number of the device (up to 32 characters) + */ + char *serial_number; + + /** + * device_type - Primary Device Type (WPS) + * Used format: categ-OUI-subcateg + * categ = Category as an integer value + * OUI = OUI and type octet as a 4-octet hex-encoded value; + * 0050F204 for default WPS OUI + * subcateg = OUI-specific Sub Category as an integer value + * Examples: + * 1-0050F204-1 (Computer / PC) + * 1-0050F204-2 (Computer / Server) + * 5-0050F204-1 (Storage / NAS) + * 6-0050F204-1 (Network Infrastructure / AP) + */ + char *device_type; + + /** + * os_version - OS Version (WPS) + * 4-octet operating system version number + */ + u8 os_version[4]; + + /** + * country - Country code + * + * This is the ISO/IEC alpha2 country code for which we are operating + * in + */ + char country[2]; + + /** + * wps_cred_processing - Credential processing + * + * 0 = process received credentials internally + * 1 = do not process received credentials; just pass them over + * ctrl_iface to external program(s) + * 2 = process received credentials internally and pass them over + * ctrl_iface to external program(s) + */ + int wps_cred_processing; +}; + + +/* Prototypes 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); +char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var); +void wpa_config_update_psk(struct wpa_ssid *ssid); +int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid); +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config, + const char *name); +void wpa_config_set_blob(struct wpa_config *config, + struct wpa_config_blob *blob); +void wpa_config_free_blob(struct wpa_config_blob *blob); +int wpa_config_remove_blob(struct wpa_config *config, const char *name); + +struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, + const char *driver_param); +#ifndef CONFIG_NO_STDOUT_DEBUG +void wpa_config_debug_dump_networks(struct wpa_config *config); +#else /* CONFIG_NO_STDOUT_DEBUG */ +#define wpa_config_debug_dump_networks(c) do { } while (0) +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +/* Prototypes for backend specific functions from the selected config_*.c */ + +/** + * wpa_config_read - Read and parse configuration database + * @name: Name of the configuration (e.g., path and file name for the + * configuration file) + * Returns: Pointer to allocated configuration data or %NULL on failure + * + * This function reads configuration data, parses its contents, and allocates + * data structures needed for storing configuration information. The allocated + * data can be freed with wpa_config_free(). + * + * Each configuration backend needs to implement this function. + */ +struct wpa_config * wpa_config_read(const char *name); + +/** + * wpa_config_write - Write or update configuration data + * @name: Name of the configuration (e.g., path and file name for the + * configuration file) + * @config: Configuration data from wpa_config_read() + * Returns: 0 on success, -1 on failure + * + * This function write all configuration data into an external database (e.g., + * a text file) in a format that can be read with wpa_config_read(). This can + * be used to allow wpa_supplicant to update its configuration, e.g., when a + * new network is added or a password is changed. + * + * Each configuration backend needs to implement this function. + */ +int wpa_config_write(const char *name, struct wpa_config *config); + +#endif /* CONFIG_H */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c new file mode 100644 index 0000000000000..29e494c1e3b01 --- /dev/null +++ b/wpa_supplicant/config_file.c @@ -0,0 +1,942 @@ +/* + * WPA Supplicant / Configuration backend: text file + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a configuration backend for text files. All the + * configuration information is stored in a text file that uses a format + * described in the sample configuration file, wpa_supplicant.conf. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "base64.h" +#include "uuid.h" +#include "eap_peer/eap_methods.h" + + +/** + * wpa_config_get_line - Read the next configuration file line + * @s: Buffer for the line + * @size: The buffer length + * @stream: File stream to read from + * @line: Pointer to a variable storing the file line number + * @_pos: Buffer for the pointer to the beginning of data on the text line or + * %NULL if not needed (returned value used instead) + * Returns: Pointer to the beginning of data on the text line or %NULL if no + * more text lines are available. + * + * This function reads the next non-empty line from the configuration file and + * removes comments. The returned string is guaranteed to be null-terminated. + */ +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, + char **_pos) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + pos = s; + + /* Skip white space from the beginning of line. */ + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + + /* Skip comment lines and empty lines */ + if (*pos == '#' || *pos == '\n' || *pos == '\0') + continue; + + /* + * Remove # comments unless they are within a double quoted + * string. + */ + sstart = os_strchr(pos, '"'); + if (sstart) + sstart = os_strrchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = os_strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + os_strlen(pos) - 1; + + /* Remove trailing white space. */ + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) + *end-- = '\0'; + + if (*pos == '\0') + continue; + + if (_pos) + *_pos = pos; + return pos; + } + + if (_pos) + *_pos = NULL; + return NULL; +} + + +static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) +{ + int errors = 0; + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: both PSK and " + "passphrase configured.", line); + errors++; + } + wpa_config_update_psk(ssid); + } + + if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) && + !ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " + "management, but no PSK configured.", line); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" + " list since it was not allowed for pairwise " + "cipher", line); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + return errors; +} + + +static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) +{ + struct wpa_ssid *ssid; + int errors = 0, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", + *line); + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = os_strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (os_strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + if (wpa_config_set(ssid, pos, pos2, *line) < 0) + errors++; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: network block was not " + "terminated properly.", *line); + errors++; + } + + errors += wpa_config_validate_network(ssid, *line); + + if (errors) { + wpa_config_free_ssid(ssid); + ssid = NULL; + } + + return ssid; +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, + const char *name) +{ + struct wpa_config_blob *blob; + char buf[256], *pos; + unsigned char *encoded = NULL, *nencoded; + int end = 0; + size_t encoded_len = 0, len; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'", + *line, name); + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + len = os_strlen(pos); + nencoded = os_realloc(encoded, encoded_len + len); + if (nencoded == NULL) { + wpa_printf(MSG_ERROR, "Line %d: not enough memory for " + "blob", *line); + os_free(encoded); + return NULL; + } + encoded = nencoded; + os_memcpy(encoded + encoded_len, pos, len); + encoded_len += len; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " + "properly", *line); + os_free(encoded); + return NULL; + } + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + os_free(encoded); + return NULL; + } + blob->name = os_strdup(name); + blob->data = base64_decode(encoded, encoded_len, &blob->len); + os_free(encoded); + + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + return NULL; + } + + return blob; +} + + +static int wpa_config_process_blob(struct wpa_config *config, FILE *f, + int *line, char *bname) +{ + char *name_end; + struct wpa_config_blob *blob; + + name_end = os_strchr(bname, '='); + if (name_end == NULL) { + wpa_printf(MSG_ERROR, "Line %d: no blob name terminator", + *line); + return -1; + } + *name_end = '\0'; + + blob = wpa_config_read_blob(f, line, bname); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s", + *line, bname); + return -1; + } + wpa_config_set_blob(config, blob); + return 0; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +struct global_parse_data { + char *name; + int (*parser)(const struct global_parse_data *data, + struct wpa_config *config, int line, const char *value); + void *param1, *param2, *param3; +}; + + +static int wpa_config_parse_int(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + int *dst; + dst = (int *) (((u8 *) config) + (long) data->param1); + *dst = atoi(pos); + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); + + if (data->param2 && *dst < (long) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param2); + *dst = (long) data->param2; + return -1; + } + + if (data->param3 && *dst > (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + return 0; +} + + +static int wpa_config_parse_str(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + char **dst, *tmp; + + len = os_strlen(pos); + if (data->param2 && len < (size_t) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param2); + return -1; + } + + if (data->param3 && len > (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param3); + return -1; + } + + tmp = os_strdup(pos); + if (tmp == NULL) + return -1; + + dst = (char **) (((u8 *) config) + (long) data->param1); + os_free(*dst); + *dst = tmp; + wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst); + + return 0; +} + + +static int wpa_config_process_country(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (!pos[0] || !pos[1]) { + wpa_printf(MSG_DEBUG, "Invalid country set"); + return -1; + } + config->country[0] = pos[0]; + config->country[1] = pos[1]; + wpa_printf(MSG_DEBUG, "country='%c%c'", + config->country[0], config->country[1]); + return 0; +} + + +static int wpa_config_process_load_dynamic_eap( + const struct global_parse_data *data, struct wpa_config *config, + int line, const char *so) +{ + int ret; + wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); + ret = eap_peer_method_load(so); + if (ret == -2) { + wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " + "reloading."); + } else if (ret) { + wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " + "method '%s'.", line, so); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_WPS + +static int wpa_config_process_uuid(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + char buf[40]; + if (uuid_str2bin(pos, config->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); + return -1; + } + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "uuid=%s", buf); + return 0; +} + + +static int wpa_config_process_os_version(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (hexstr2bin(pos, config->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); + return -1; + } + wpa_printf(MSG_DEBUG, "os_version=%08x", + WPA_GET_BE32(config->os_version)); + return 0; +} + +#endif /* CONFIG_WPS */ + + +#ifdef OFFSET +#undef OFFSET +#endif /* OFFSET */ +/* OFFSET: Get offset of a variable within the wpa_config structure */ +#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) + +#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL +#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL +#define _INT(f) #f, wpa_config_parse_int, OFFSET(f) +#define INT(f) _INT(f), NULL, NULL +#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max +#define _STR(f) #f, wpa_config_parse_str, OFFSET(f) +#define STR(f) _STR(f), NULL, NULL +#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max + +static const struct global_parse_data global_fields[] = { +#ifdef CONFIG_CTRL_IFACE + { STR(ctrl_interface) }, + { STR(ctrl_interface_group) } /* deprecated */, +#endif /* CONFIG_CTRL_IFACE */ + { INT_RANGE(eapol_version, 1, 2) }, + { INT(ap_scan) }, + { INT(fast_reauth) }, +#ifdef EAP_TLS_OPENSSL + { STR(opensc_engine_path) }, + { STR(pkcs11_engine_path) }, + { STR(pkcs11_module_path) }, +#endif /* EAP_TLS_OPENSSL */ + { STR(driver_param) }, + { INT(dot11RSNAConfigPMKLifetime) }, + { INT(dot11RSNAConfigPMKReauthThreshold) }, + { INT(dot11RSNAConfigSATimeout) }, +#ifndef CONFIG_NO_CONFIG_WRITE + { INT(update_config) }, +#endif /* CONFIG_NO_CONFIG_WRITE */ + { FUNC_NO_VAR(load_dynamic_eap) }, +#ifdef CONFIG_WPS + { FUNC(uuid) }, + { STR_RANGE(device_name, 0, 32) }, + { STR_RANGE(manufacturer, 0, 64) }, + { STR_RANGE(model_name, 0, 32) }, + { STR_RANGE(model_number, 0, 32) }, + { STR_RANGE(serial_number, 0, 32) }, + { STR(device_type) }, + { FUNC(os_version) }, + { INT_RANGE(wps_cred_processing, 0, 2) }, +#endif /* CONFIG_WPS */ + { FUNC(country) } +}; + +#undef FUNC +#undef _INT +#undef INT +#undef INT_RANGE +#undef _STR +#undef STR +#undef STR_RANGE +#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) + + +static int wpa_config_process_global(struct wpa_config *config, char *pos, + int line) +{ + size_t i; + int ret = 0; + + for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { + const struct global_parse_data *field = &global_fields[i]; + size_t flen = os_strlen(field->name); + if (os_strncmp(pos, field->name, flen) != 0 || + pos[flen] != '=') + continue; + + if (field->parser(field, config, line, pos + flen + 1)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse '%s'.", line, pos); + ret = -1; + } + break; + } + if (i == NUM_GLOBAL_FIELDS) { + wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", + line, pos); + ret = -1; + } + + return ret; +} + + +struct wpa_config * wpa_config_read(const char *name) +{ + FILE *f; + char buf[256], *pos; + int errors = 0, line = 0; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_config *config; + int id = 0; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); + f = fopen(name, "r"); + if (f == NULL) { + os_free(config); + return NULL; + } + + while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { + if (os_strcmp(pos, "network={") == 0) { + ssid = wpa_config_read_network(f, &line, id++); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse network block.", line); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Line %d: failed to add " + "network block to priority list.", + line); + errors++; + continue; + } +#ifndef CONFIG_NO_CONFIG_BLOBS + } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { + if (wpa_config_process_blob(config, f, &line, pos + 12) + < 0) { + errors++; + continue; + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + } else if (wpa_config_process_global(config, pos, line) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " + "line '%s'.", line, pos); + errors++; + continue; + } + } + + fclose(f); + + config->ssid = head; + wpa_config_debug_dump_networks(config); + + if (errors) { + wpa_config_free(config); + config = NULL; + head = NULL; + } + + return config; +} + + +#ifndef CONFIG_NO_CONFIG_WRITE + +static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, field); + if (value == NULL) + return; + fprintf(f, "\t%s=%s\n", field, value); + os_free(value); +} + + +static void write_int(FILE *f, const char *field, int value, int def) +{ + if (value == def) + return; + fprintf(f, "\t%s=%d\n", field, value); +} + + +static void write_bssid(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid"); + if (value == NULL) + return; + fprintf(f, "\tbssid=%s\n", value); + os_free(value); +} + + +static void write_psk(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "psk"); + if (value == NULL) + return; + fprintf(f, "\tpsk=%s\n", value); + os_free(value); +} + + +static void write_proto(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->proto == DEFAULT_PROTO) + return; + + value = wpa_config_get(ssid, "proto"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tproto=%s\n", value); + os_free(value); +} + + +static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + return; + + value = wpa_config_get(ssid, "key_mgmt"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tkey_mgmt=%s\n", value); + os_free(value); +} + + +static void write_pairwise(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) + return; + + value = wpa_config_get(ssid, "pairwise"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tpairwise=%s\n", value); + os_free(value); +} + + +static void write_group(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->group_cipher == DEFAULT_GROUP) + return; + + value = wpa_config_get(ssid, "group"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tgroup=%s\n", value); + os_free(value); +} + + +static void write_auth_alg(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->auth_alg == 0) + return; + + value = wpa_config_get(ssid, "auth_alg"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tauth_alg=%s\n", value); + os_free(value); +} + + +#ifdef IEEE8021X_EAPOL +static void write_eap(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + value = wpa_config_get(ssid, "eap"); + if (value == NULL) + return; + + if (value[0]) + fprintf(f, "\teap=%s\n", value); + os_free(value); +} +#endif /* IEEE8021X_EAPOL */ + + +static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + int res; + + res = os_snprintf(field, sizeof(field), "wep_key%d", idx); + if (res < 0 || (size_t) res >= sizeof(field)) + return; + value = wpa_config_get(ssid, field); + if (value) { + fprintf(f, "\t%s=%s\n", field, value); + os_free(value); + } +} + + +static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) +{ + int i; + +#define STR(t) write_str(f, #t, ssid) +#define INT(t) write_int(f, #t, ssid->t, 0) +#define INTe(t) write_int(f, #t, ssid->eap.t, 0) +#define INT_DEF(t, def) write_int(f, #t, ssid->t, def) +#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def) + + STR(ssid); + INT(scan_ssid); + write_bssid(f, ssid); + write_psk(f, ssid); + write_proto(f, ssid); + write_key_mgmt(f, ssid); + write_pairwise(f, ssid); + write_group(f, ssid); + write_auth_alg(f, ssid); +#ifdef IEEE8021X_EAPOL + write_eap(f, ssid); + STR(identity); + STR(anonymous_identity); + STR(password); + STR(ca_cert); + STR(ca_path); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + STR(ca_path2); + STR(client_cert2); + STR(private_key2); + STR(private_key2_passwd); + STR(dh_file2); + STR(subject_match2); + STR(altsubject_match2); + STR(phase1); + STR(phase2); + STR(pcsc); + STR(pin); + STR(engine_id); + STR(key_id); + STR(cert_id); + STR(ca_cert_id); + STR(key2_id); + STR(pin2); + STR(engine2_id); + STR(cert2_id); + STR(ca_cert2_id); + INTe(engine); + INTe(engine2); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); +#endif /* IEEE8021X_EAPOL */ + for (i = 0; i < 4; i++) + write_wep_key(f, i, ssid); + INT(wep_tx_keyidx); + INT(priority); +#ifdef IEEE8021X_EAPOL + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); +#endif /* IEEE8021X_EAPOL */ + INT(mode); + INT(proactive_key_caching); + INT(disabled); + INT(peerkey); +#ifdef CONFIG_IEEE80211W + INT(ieee80211w); +#endif /* CONFIG_IEEE80211W */ + STR(id_str); + +#undef STR +#undef INT +#undef INT_DEF +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) +{ + unsigned char *encoded; + + encoded = base64_encode(blob->data, blob->len, NULL); + if (encoded == NULL) + return -1; + + fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded); + os_free(encoded); + return 0; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +static void wpa_config_write_global(FILE *f, struct wpa_config *config) +{ +#ifdef CONFIG_CTRL_IFACE + if (config->ctrl_interface) + fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface); + if (config->ctrl_interface_group) + fprintf(f, "ctrl_interface_group=%s\n", + config->ctrl_interface_group); +#endif /* CONFIG_CTRL_IFACE */ + if (config->eapol_version != DEFAULT_EAPOL_VERSION) + fprintf(f, "eapol_version=%d\n", config->eapol_version); + if (config->ap_scan != DEFAULT_AP_SCAN) + fprintf(f, "ap_scan=%d\n", config->ap_scan); + if (config->fast_reauth != DEFAULT_FAST_REAUTH) + fprintf(f, "fast_reauth=%d\n", config->fast_reauth); +#ifdef EAP_TLS_OPENSSL + 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); +#endif /* EAP_TLS_OPENSSL */ + if (config->driver_param) + fprintf(f, "driver_param=%s\n", config->driver_param); + if (config->dot11RSNAConfigPMKLifetime) + fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", + config->dot11RSNAConfigPMKLifetime); + if (config->dot11RSNAConfigPMKReauthThreshold) + fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", + config->dot11RSNAConfigPMKReauthThreshold); + if (config->dot11RSNAConfigSATimeout) + fprintf(f, "dot11RSNAConfigSATimeout=%d\n", + config->dot11RSNAConfigSATimeout); + if (config->update_config) + fprintf(f, "update_config=%d\n", config->update_config); +#ifdef CONFIG_WPS + if (!is_nil_uuid(config->uuid)) { + char buf[40]; + uuid_bin2str(config->uuid, buf, sizeof(buf)); + fprintf(f, "uuid=%s\n", buf); + } + if (config->device_name) + fprintf(f, "device_name=%s\n", config->device_name); + if (config->manufacturer) + fprintf(f, "manufacturer=%s\n", config->manufacturer); + if (config->model_name) + fprintf(f, "model_name=%s\n", config->model_name); + if (config->model_number) + fprintf(f, "model_number=%s\n", config->model_number); + if (config->serial_number) + fprintf(f, "serial_number=%s\n", config->serial_number); + if (config->device_type) + fprintf(f, "device_type=%s\n", config->device_type); + if (WPA_GET_BE32(config->os_version)) + fprintf(f, "os_version=%08x\n", + WPA_GET_BE32(config->os_version)); + if (config->wps_cred_processing) + fprintf(f, "wps_cred_processing=%d\n", + config->wps_cred_processing); +#endif /* CONFIG_WPS */ + if (config->country[0] && config->country[1]) { + fprintf(f, "country=%c%c\n", + config->country[0], config->country[1]); + } +} + +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ +#ifndef CONFIG_NO_CONFIG_WRITE + FILE *f; + struct wpa_ssid *ssid; +#ifndef CONFIG_NO_CONFIG_BLOBS + struct wpa_config_blob *blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ + int ret = 0; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + + f = fopen(name, "w"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); + return -1; + } + + wpa_config_write_global(f, config); + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + continue; /* do not save temporary WPS networks */ + fprintf(f, "\nnetwork={\n"); + wpa_config_write_network(f, ssid); + fprintf(f, "}\n"); + } + +#ifndef CONFIG_NO_CONFIG_BLOBS + for (blob = config->blobs; blob; blob = blob->next) { + ret = wpa_config_write_blob(f, blob); + if (ret) + break; + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + fclose(f); + + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", + name, ret ? "un" : ""); + return ret; +#else /* CONFIG_NO_CONFIG_WRITE */ + return -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ +} diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c new file mode 100644 index 0000000000000..2e9ccc0a28354 --- /dev/null +++ b/wpa_supplicant/config_none.c @@ -0,0 +1,57 @@ +/* + * WPA Supplicant / Configuration backend: empty starting point + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements dummy example of a configuration backend. None of the + * functions are actually implemented so this can be used as a simple + * compilation test or a starting point for a new configuration backend. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "base64.h" + + +struct wpa_config * wpa_config_read(const char *name) +{ + struct wpa_config *config; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + /* TODO: fill in configuration data */ + return config; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + + /* TODO: write global config parameters */ + + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + /* TODO: write networks */ + } + + for (blob = config->blobs; blob; blob = blob->next) { + /* TODO: write blobs */ + } + + return 0; +} diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h new file mode 100644 index 0000000000000..5510639c147cd --- /dev/null +++ b/wpa_supplicant/config_ssid.h @@ -0,0 +1,347 @@ +/* + * WPA Supplicant / Network configuration structures + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CONFIG_SSID_H +#define CONFIG_SSID_H + +#include "defs.h" +#include "eap_peer/eap_config.h" + +#define MAX_SSID_LEN 32 + + +#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1) +#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \ + EAPOL_FLAG_REQUIRE_KEY_BROADCAST) +#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN) +#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X) +#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) +#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \ + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) +#define DEFAULT_FRAGMENT_SIZE 1398 + +/** + * struct wpa_ssid - Network configuration data + * + * This structure includes all the configuration variables for a network. This + * data is included in the per-interface configuration data as an element of + * the network list, struct wpa_config::ssid. Each network block in the + * configuration is mapped to a struct wpa_ssid instance. + */ +struct wpa_ssid { + /** + * next - Next network in global list + * + * This pointer can be used to iterate over all networks. The head of + * this list is stored in the ssid field of struct wpa_config. + */ + struct wpa_ssid *next; + + /** + * pnext - Next network in per-priority list + * + * This pointer can be used to iterate over all networks in the same + * priority class. The heads of these list are stored in the pssid + * fields of struct wpa_config. + */ + struct wpa_ssid *pnext; + + /** + * id - Unique id for the network + * + * This identifier is used as a unique identifier for each network + * block when using the control interface. Each network is allocated an + * id when it is being created, either when reading the configuration + * file or when a new network is added through the control interface. + */ + int id; + + /** + * priority - Priority group + * + * By default, all networks will get same priority group (0). If some + * of the networks are more desirable, this field can be used to change + * the order in which wpa_supplicant goes through the networks when + * selecting a BSS. The priority groups will be iterated in decreasing + * priority (i.e., the larger the priority value, the sooner the + * network is matched against the scan results). Within each priority + * group, networks will be selected based on security policy, signal + * strength, etc. + * + * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are + * not using this priority to select the order for scanning. Instead, + * they try the networks in the order that used in the configuration + * file. + */ + int priority; + + /** + * ssid - Service set identifier (network name) + * + * This is the SSID for the network. For wireless interfaces, this is + * used to select which network will be used. If set to %NULL (or + * ssid_len=0), any SSID can be used. For wired interfaces, this must + * be set to %NULL. Note: SSID may contain any characters, even nul + * (ASCII 0) and as such, this should not be assumed to be a nul + * terminated string. ssid_len defines how many characters are valid + * and the ssid field is not guaranteed to be nul terminated. + */ + u8 *ssid; + + /** + * ssid_len - Length of the SSID + */ + size_t ssid_len; + + /** + * bssid - BSSID + * + * If set, this network block is used only when associating with the AP + * using the configured BSSID + */ + u8 bssid[ETH_ALEN]; + + /** + * bssid_set - Whether BSSID is configured for this network + */ + int bssid_set; + + /** + * psk - WPA pre-shared key (256 bits) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured + */ + int psk_set; + + /** + * passphrase - WPA ASCII passphrase + * + * If this is set, psk will be generated using the SSID and passphrase + * configured for the network. ASCII passphrase must be between 8 and + * 63 characters (inclusive). + */ + char *passphrase; + + /** + * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* + */ + int pairwise_cipher; + + /** + * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_* + */ + int group_cipher; + + /** + * key_mgmt - Bitfield of allowed key management protocols + * + * WPA_KEY_MGMT_* + */ + int key_mgmt; + + /** + * proto - Bitfield of allowed protocols, WPA_PROTO_* + */ + int proto; + + /** + * auth_alg - Bitfield of allowed authentication algorithms + * + * WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * scan_ssid - Scan this SSID with Probe Requests + * + * scan_ssid can be used to scan for APs using hidden SSIDs. + * Note: Many drivers do not support this. ap_mode=2 can be used with + * such drivers to use hidden SSIDs. + */ + int scan_ssid; + +#ifdef IEEE8021X_EAPOL +#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1) + /** + * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*) + */ + int eapol_flags; + + /** + * eap - EAP peer configuration for this network + */ + struct eap_peer_config eap; +#endif /* IEEE8021X_EAPOL */ + +#define NUM_WEP_KEYS 4 +#define MAX_WEP_KEY_LEN 16 + /** + * wep_key - WEP keys + */ + u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN]; + + /** + * wep_key_len - WEP key lengths + */ + size_t wep_key_len[NUM_WEP_KEYS]; + + /** + * wep_tx_keyidx - Default key index for TX frames using WEP + */ + int wep_tx_keyidx; + + /** + * proactive_key_caching - Enable proactive key caching + * + * This field can be used to enable proactive key caching which is also + * known as opportunistic PMKSA caching for WPA2. This is disabled (0) + * by default. Enable by setting this to 1. + * + * Proactive key caching is used to make supplicant assume that the APs + * are using the same PMK and generate PMKSA cache entries without + * doing RSN pre-authentication. This requires support from the AP side + * and is normally used with wireless switches that co-locate the + * authenticator. + */ + int proactive_key_caching; + + /** + * mixed_cell - Whether mixed cells are allowed + * + * This option can be used to configure whether so called mixed cells, + * i.e., networks that use both plaintext and encryption in the same + * SSID, are allowed. This is disabled (0) by default. Enable by + * setting this to 1. + */ + int mixed_cell; + +#ifdef IEEE8021X_EAPOL + + /** + * leap - Number of EAP methods using LEAP + * + * This field should be set to 1 if LEAP is enabled. This is used to + * select IEEE 802.11 authentication algorithm. + */ + int leap; + + /** + * non_leap - Number of EAP methods not using LEAP + * + * This field should be set to >0 if any EAP method other than LEAP is + * enabled. This is used to select IEEE 802.11 authentication + * algorithm. + */ + int non_leap; + + /** + * eap_workaround - EAP workarounds enabled + * + * wpa_supplicant supports number of "EAP workarounds" to work around + * interoperability issues with incorrectly behaving authentication + * servers. This is recommended to be enabled by default because some + * of the issues are present in large number of authentication servers. + * + * Strict EAP conformance mode can be configured by disabling + * workarounds with eap_workaround = 0. + */ + unsigned int eap_workaround; + +#endif /* IEEE8021X_EAPOL */ + + /** + * mode - IEEE 802.11 operation mode (Infrastucture/IBSS) + * + * 0 = infrastructure (Managed) mode, i.e., associate with an AP. + * + * 1 = IBSS (ad-hoc, peer-to-peer) + * + * 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; + + /** + * 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; + + /** + * peerkey - Whether PeerKey handshake for direct links is allowed + * + * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are + * enabled. + * + * 0 = disabled (default) + * 1 = enabled + */ + int peerkey; + + /** + * id_str - Network identifier string for external scripts + * + * This value is passed to external ctrl_iface monitors in + * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR + * environment variable for action scripts. + */ + char *id_str; + +#ifdef CONFIG_IEEE80211W + /** + * ieee80211w - Whether management frame protection is enabled + * + * This value is used to configure policy for management frame + * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required. + */ + enum { + NO_IEEE80211W = 0, + IEEE80211W_OPTIONAL = 1, + IEEE80211W_REQUIRED = 2 + } ieee80211w; +#endif /* CONFIG_IEEE80211W */ + + /** + * frequency - Channel frequency in megahertz (MHz) for IBSS + * + * This value is used to configure the initial channel for IBSS (adhoc) + * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in + * the infrastructure mode. In addition, this value is only used by the + * station that creates the IBSS. If an IBSS network with the + * configured SSID is already present, the frequency of the network + * will be used instead of this configured value. + */ + int frequency; + + /** + * wpa_ptk_rekey - Maximum lifetime for PTK in seconds + * + * This value can be used to enforce rekeying of PTK to mitigate some + * attacks against TKIP deficiencies. + */ + int wpa_ptk_rekey; +}; + +#endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c new file mode 100644 index 0000000000000..456d41792245e --- /dev/null +++ b/wpa_supplicant/config_winreg.c @@ -0,0 +1,980 @@ +/* + * WPA Supplicant / Configuration backend: Windows registry + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a configuration backend for Windows registry. All the + * configuration information is stored in the registry and the format for + * network configuration fields is same as described in the sample + * configuration file, wpa_supplicant.conf. + * + * Configuration data is in + * \a HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant\\configs + * key. Each configuration profile has its own key under this. In terms of text + * files, each profile would map to a separate text file with possibly multiple + * networks. Under each profile, there is a networks key that lists all + * networks as a subkey. Each network has set of values in the same way as + * network block in the configuration file. In addition, blobs subkey has + * possible blobs as values. + * + * Example network configuration block: + * \verbatim +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK +\endverbatim + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" +#include "config.h" + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk) +{ + struct wpa_config_blob *blob; + int errors = 0; + HKEY bhk; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "blobs key"); + return 0; /* assume no blobs */ + } + + for (i = 0; ; i++) { +#define TNAMELEN 255 + TCHAR name[TNAMELEN]; + char data[4096]; + DWORD namelen, datalen, type; + + namelen = TNAMELEN; + datalen = sizeof(data); + ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= TNAMELEN) + namelen = TNAMELEN - 1; + name[namelen] = TEXT('\0'); + wpa_unicode2ascii_inplace(name); + + if (datalen >= sizeof(data)) + datalen = sizeof(data) - 1; + + wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d", + (int) i, name, (int) datalen); + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + errors++; + break; + } + blob->name = os_strdup((char *) name); + blob->data = os_malloc(datalen); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + errors++; + break; + } + os_memcpy(blob->data, data, datalen); + blob->len = datalen; + + wpa_config_set_blob(config, blob); + } + + RegCloseKey(bhk); + + return errors ? -1 : 0; +} + + +static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val) +{ + DWORD val, buflen; + LONG ret; + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val); + *_val = val; + return 0; + } + + return -1; +} + + +static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name) +{ + DWORD buflen; + LONG ret; + TCHAR *val; + + buflen = 0; + ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen); + if (ret != ERROR_SUCCESS) + return NULL; + val = os_malloc(buflen); + if (val == NULL) + return NULL; + + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen); + if (ret != ERROR_SUCCESS) { + os_free(val); + return NULL; + } + + wpa_unicode2ascii_inplace(val); + wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val); + return (char *) val; +} + + +#ifdef CONFIG_WPS +static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("uuid")); + if (str == NULL) + return 0; + + if (uuid_str2bin(str, config->uuid)) + ret = -1; + + os_free(str); + + return ret; +} + + +static int wpa_config_read_global_os_version(struct wpa_config *config, + HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("os_version")); + if (str == NULL) + return 0; + + if (hexstr2bin(str, config->os_version, 4)) + ret = -1; + + os_free(str); + + return ret; +} +#endif /* CONFIG_WPS */ + + +static int wpa_config_read_global(struct wpa_config *config, HKEY hk) +{ + int errors = 0; + + wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan); + wpa_config_read_reg_dword(hk, TEXT("fast_reauth"), + &config->fast_reauth); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + (int *) &config->dot11RSNAConfigPMKLifetime); + wpa_config_read_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + (int *) + &config->dot11RSNAConfigPMKReauthThreshold); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + (int *) &config->dot11RSNAConfigSATimeout); + wpa_config_read_reg_dword(hk, TEXT("update_config"), + &config->update_config); + + if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"), + &config->eapol_version) == 0) { + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)", + config->eapol_version); + errors++; + } + } + + config->ctrl_interface = wpa_config_read_reg_string( + hk, TEXT("ctrl_interface")); + +#ifdef CONFIG_WPS + if (wpa_config_read_global_uuid(config, hk)) + errors++; + config->device_name = wpa_config_read_reg_string( + hk, TEXT("device_name")); + config->manufacturer = wpa_config_read_reg_string( + hk, TEXT("manufacturer")); + config->model_name = wpa_config_read_reg_string( + hk, TEXT("model_name")); + config->serial_number = wpa_config_read_reg_string( + hk, TEXT("serial_number")); + config->device_type = wpa_config_read_reg_string( + hk, TEXT("device_type")); + if (wpa_config_read_global_os_version(config, hk)) + errors++; + wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), + &config->wps_cred_processing); +#endif /* CONFIG_WPS */ + + return errors ? -1 : 0; +} + + +static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, + int id) +{ + HKEY nhk; + LONG ret; + DWORD i; + struct wpa_ssid *ssid; + int errors = 0; + + ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "network '" TSTR "'", netw); + return NULL; + } + + wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw); + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) { + RegCloseKey(nhk); + return NULL; + } + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + for (i = 0; ; i++) { + TCHAR name[255], data[1024]; + DWORD namelen, datalen, type; + + namelen = 255; + datalen = sizeof(data); + ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + if (datalen >= 1024) + datalen = 1024 - 1; + data[datalen] = TEXT('\0'); + + wpa_unicode2ascii_inplace(name); + wpa_unicode2ascii_inplace(data); + if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0) + errors++; + } + + RegCloseKey(nhk); + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Both PSK and passphrase " + "configured for network '" TSTR "'.", netw); + errors++; + } + wpa_config_update_psk(ssid); + } + + if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) && + !ssid->psk_set) { + wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, " + "but no PSK configured for network '" TSTR "'.", + netw); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher " + "list since it was not allowed for pairwise " + "cipher for network '" TSTR "'.", netw); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + if (errors) { + wpa_config_free_ssid(ssid); + ssid = NULL; + } + + return ssid; +} + + +static int wpa_config_read_networks(struct wpa_config *config, HKEY hk) +{ + HKEY nhk; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + int errors = 0; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS, + &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks " + "registry key"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + ssid = wpa_config_read_network(nhk, name, i); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Failed to parse network " + "profile '%s'.", name); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Failed to add network profile " + "'%s' to priority list.", name); + errors++; + continue; + } + } + + RegCloseKey(nhk); + + config->ssid = head; + + return errors ? -1 : 0; +} + + +struct wpa_config * wpa_config_read(const char *name) +{ + TCHAR buf[256]; + int errors = 0; + struct wpa_config *config; + HKEY hk; + LONG ret; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry HKLM\\" TSTR, buf); + os_free(config); + return NULL; + } + + if (wpa_config_read_global(config, hk)) + errors++; + + if (wpa_config_read_networks(config, hk)) + errors++; + + if (wpa_config_read_blobs(config, hk)) + errors++; + + wpa_config_debug_dump_networks(config); + + RegCloseKey(hk); + + if (errors) { + wpa_config_free(config); + config = NULL; + } + + return config; +} + + +static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val, + int def) +{ + LONG ret; + DWORD _val = val; + + if (val == def) { + RegDeleteValue(hk, name); + return 0; + } + + ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val, + sizeof(_val)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d", + name, val, (int) GetLastError()); + return -1; + } + + return 0; +} + + +static int wpa_config_write_reg_string(HKEY hk, const char *name, + const char *val) +{ + LONG ret; + TCHAR *_name, *_val; + + _name = wpa_strdup_tchar(name); + if (_name == NULL) + return -1; + + if (val == NULL) { + RegDeleteValue(hk, _name); + os_free(_name); + return 0; + } + + _val = wpa_strdup_tchar(val); + if (_val == NULL) { + os_free(_name); + return -1; + } + ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val, + (os_strlen(val) + 1) * sizeof(TCHAR)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': " + "error %d", name, val, (int) GetLastError()); + os_free(_name); + os_free(_val); + return -1; + } + + os_free(_name); + os_free(_val); + return 0; +} + + +static int wpa_config_write_global(struct wpa_config *config, HKEY hk) +{ +#ifdef CONFIG_CTRL_IFACE + wpa_config_write_reg_string(hk, "ctrl_interface", + config->ctrl_interface); +#endif /* CONFIG_CTRL_IFACE */ + + wpa_config_write_reg_dword(hk, TEXT("eapol_version"), + config->eapol_version, + DEFAULT_EAPOL_VERSION); + wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan, + DEFAULT_AP_SCAN); + wpa_config_write_reg_dword(hk, TEXT("fast_reauth"), + config->fast_reauth, DEFAULT_FAST_REAUTH); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + config->dot11RSNAConfigPMKLifetime, 0); + wpa_config_write_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + config->dot11RSNAConfigPMKReauthThreshold, + 0); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + config->dot11RSNAConfigSATimeout, 0); + wpa_config_write_reg_dword(hk, TEXT("update_config"), + config->update_config, + 0); +#ifdef CONFIG_WPS + if (!is_nil_uuid(config->uuid)) { + char buf[40]; + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_config_write_reg_string(hk, "uuid", buf); + } + wpa_config_write_reg_string(hk, "device_name", config->device_name); + wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer); + wpa_config_write_reg_string(hk, "model_name", config->model_name); + wpa_config_write_reg_string(hk, "model_number", config->model_number); + wpa_config_write_reg_string(hk, "serial_number", + config->serial_number); + wpa_config_write_reg_string(hk, "device_type", config->device_type); + if (WPA_GET_BE32(config->os_version)) { + char vbuf[10]; + os_snprintf(vbuf, sizeof(vbuf), "%08x", + WPA_GET_BE32(config->os_version)); + wpa_config_write_reg_string(hk, "os_version", vbuf); + } + wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), + config->wps_cred_processing, 0); +#endif /* CONFIG_WPS */ + + return 0; +} + + +static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key) +{ + HKEY nhk; + int i, errors = 0; + LONG ret; + + ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR + "' for subkey deletion: error 0x%x (%d)", key, + (unsigned int) ret, (int) GetLastError()); + return 0; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + ret = RegDeleteKey(nhk, name); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + errors++; + } + } + + RegCloseKey(nhk); + + return errors ? -1 : 0; +} + + +static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, field); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, field, value); + os_free(value); +} + + +static void write_int(HKEY hk, const char *field, int value, int def) +{ + char val[20]; + if (value == def) + return; + os_snprintf(val, sizeof(val), "%d", value); + wpa_config_write_reg_string(hk, field, val); +} + + +static void write_bssid(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "bssid", value); + os_free(value); +} + + +static void write_psk(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "psk"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "psk", value); + os_free(value); +} + + +static void write_proto(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->proto == DEFAULT_PROTO) + return; + + value = wpa_config_get(ssid, "proto"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "proto", value); + os_free(value); +} + + +static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + return; + + value = wpa_config_get(ssid, "key_mgmt"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "key_mgmt", value); + os_free(value); +} + + +static void write_pairwise(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) + return; + + value = wpa_config_get(ssid, "pairwise"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "pairwise", value); + os_free(value); +} + + +static void write_group(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->group_cipher == DEFAULT_GROUP) + return; + + value = wpa_config_get(ssid, "group"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "group", value); + os_free(value); +} + + +static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->auth_alg == 0) + return; + + value = wpa_config_get(ssid, "auth_alg"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "auth_alg", value); + os_free(value); +} + + +#ifdef IEEE8021X_EAPOL +static void write_eap(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + value = wpa_config_get(ssid, "eap"); + if (value == NULL) + return; + + if (value[0]) + wpa_config_write_reg_string(hk, "eap", value); + os_free(value); +} +#endif /* IEEE8021X_EAPOL */ + + +static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + + os_snprintf(field, sizeof(field), "wep_key%d", idx); + value = wpa_config_get(ssid, field); + if (value) { + wpa_config_write_reg_string(hk, field, value); + os_free(value); + } +} + + +static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) +{ + int i, errors = 0; + HKEY nhk, netw; + LONG ret; + TCHAR name[5]; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key " + "for subkey addition: error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return 0; + } + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw, + NULL); + RegCloseKey(nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':" + " error 0x%x (%d)", + name, (unsigned int) ret, (int) GetLastError()); + return -1; + } + +#define STR(t) write_str(netw, #t, ssid) +#define INT(t) write_int(netw, #t, ssid->t, 0) +#define INTe(t) write_int(netw, #t, ssid->eap.t, 0) +#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def) +#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def) + + STR(ssid); + INT(scan_ssid); + write_bssid(netw, ssid); + write_psk(netw, ssid); + write_proto(netw, ssid); + write_key_mgmt(netw, ssid); + write_pairwise(netw, ssid); + write_group(netw, ssid); + write_auth_alg(netw, ssid); +#ifdef IEEE8021X_EAPOL + write_eap(netw, ssid); + STR(identity); + STR(anonymous_identity); + STR(password); + STR(ca_cert); + STR(ca_path); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + STR(ca_path2); + STR(client_cert2); + STR(private_key2); + STR(private_key2_passwd); + STR(dh_file2); + STR(subject_match2); + STR(altsubject_match2); + STR(phase1); + STR(phase2); + STR(pcsc); + STR(pin); + STR(engine_id); + STR(key_id); + STR(cert_id); + STR(ca_cert_id); + STR(key2_id); + STR(pin2); + STR(engine2_id); + STR(cert2_id); + STR(ca_cert2_id); + INTe(engine); + INTe(engine2); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); +#endif /* IEEE8021X_EAPOL */ + for (i = 0; i < 4; i++) + write_wep_key(netw, i, ssid); + INT(wep_tx_keyidx); + INT(priority); +#ifdef IEEE8021X_EAPOL + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); +#endif /* IEEE8021X_EAPOL */ + INT(mode); + INT(proactive_key_caching); + INT(disabled); + INT(peerkey); +#ifdef CONFIG_IEEE80211W + INT(ieee80211w); +#endif /* CONFIG_IEEE80211W */ + STR(id_str); + +#undef STR +#undef INT +#undef INT_DEF + + RegCloseKey(netw); + + return errors ? -1 : 0; +} + + +static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob) +{ + HKEY bhk; + LONG ret; + TCHAR *name; + + ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL, + &bhk, NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: " + "error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return -1; + } + + name = wpa_strdup_tchar(blob->name); + ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data, + blob->len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': " + "error 0x%x (%d)", blob->name, (unsigned int) ret, + (int) GetLastError()); + RegCloseKey(bhk); + os_free(name); + return -1; + } + os_free(name); + + RegCloseKey(bhk); + + return 0; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + TCHAR buf[256]; + HKEY hk; + LONG ret; + int errors = 0; + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + int id; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry %s: error %d", buf, + (int) GetLastError()); + return -1; + } + + if (wpa_config_write_global(config, hk)) { + wpa_printf(MSG_ERROR, "Failed to write global configuration " + "data"); + errors++; + } + + wpa_config_delete_subkeys(hk, TEXT("networks")); + for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) { + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + continue; /* do not save temporary WPS networks */ + if (wpa_config_write_network(hk, ssid, id)) + errors++; + } + + RegDeleteKey(hk, TEXT("blobs")); + for (blob = config->blobs; blob; blob = blob->next) { + if (wpa_config_write_blob(hk, blob)) + errors++; + } + + RegCloseKey(hk); + + wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully", + name, errors ? "un" : ""); + return errors ? -1 : 0; +} diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c new file mode 100644 index 0000000000000..2b737bc1aa560 --- /dev/null +++ b/wpa_supplicant/ctrl_iface.c @@ -0,0 +1,1875 @@ +/* + * WPA Supplicant / Control interface (shared code for all backends) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "wpa.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "l2_packet/l2_packet.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_ctrl.h" +#include "eap_peer/eap.h" +#include "ieee802_11_defs.h" +#include "wps_supplicant.h" +#include "wps/wps.h" + +static int wpa_supplicant_global_iface_list(struct wpa_global *global, + char *buf, int len); +static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + char *buf, int len); + + +static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *value; + int ret = 0; + + value = os_strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); + if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + atoi(value), -1, -1, -1); + } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, atoi(value), -1, -1); + } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, atoi(value), -1); + } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, -1, atoi(value)); + } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, + atoi(value))) + ret = -1; + } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == + 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, + atoi(value))) + ret = -1; + } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) + ret = -1; + } else + ret = -1; + + return ret; +} + + +#ifdef IEEE8021X_EAPOL +static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " + "'%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); + rsn_preauth_deinit(wpa_s->wpa); + if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL)) + return -1; + + return 0; +} +#endif /* IEEE8021X_EAPOL */ + + +#ifdef CONFIG_PEERKEY +/* MLME-STKSTART.request(peer) */ +static int wpa_supplicant_ctrl_iface_stkstart( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid " + "address '%s'", peer); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR, + MAC2STR(peer)); + + return wpa_sm_stkstart(wpa_s->wpa, peer); +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_IEEE80211R +static int wpa_supplicant_ctrl_iface_ft_ds( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 target_ap[ETH_ALEN]; + + if (hwaddr_aton(addr, target_ap)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " + "address '%s'", target_ap); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap)); + + return wpa_ft_start_over_ds(wpa_s->wpa, target_ap); +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_WPS +static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN]; + + if (cmd == NULL || os_strcmp(cmd, "any") == 0) + return wpas_wps_start_pbc(wpa_s, NULL); + + if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", + cmd); + return -1; + } + + return wpas_wps_start_pbc(wpa_s, bssid); +} + + +static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + char *pin; + int ret; + + pin = os_strchr(cmd, ' '); + if (pin) + *pin++ = '\0'; + + if (os_strcmp(cmd, "any") == 0) + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", + cmd); + return -1; + } + + if (pin) { + ret = wpas_wps_start_pin(wpa_s, _bssid, pin); + if (ret < 0) + return -1; + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL); + if (ret < 0) + return -1; + + /* Return the generated PIN */ + ret = os_snprintf(buf, buflen, "%08d", ret); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + char *pin; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + if (os_strcmp(cmd, "any") == 0) + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", + cmd); + return -1; + } + + return wpas_wps_start_reg(wpa_s, _bssid, pin); +} +#endif /* CONFIG_WPS */ + + +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, + char *rsp) +{ +#ifdef IEEE8021X_EAPOL + char *pos, *id_pos; + int id; + struct wpa_ssid *ssid; + struct eap_peer_config *eap; + + pos = os_strchr(rsp, '-'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id_pos = pos; + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(id_pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) pos, os_strlen(pos)); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + eap = &ssid->eap; + + if (os_strcmp(rsp, "IDENTITY") == 0) { + os_free(eap->identity); + eap->identity = (u8 *) os_strdup(pos); + eap->identity_len = os_strlen(pos); + eap->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "PASSWORD") == 0) { + os_free(eap->password); + eap->password = (u8 *) os_strdup(pos); + eap->password_len = os_strlen(pos); + eap->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) { + os_free(eap->new_password); + eap->new_password = (u8 *) os_strdup(pos); + eap->new_password_len = os_strlen(pos); + eap->pending_req_new_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "PIN") == 0) { + os_free(eap->pin); + eap->pin = os_strdup(pos); + eap->pending_req_pin = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "OTP") == 0) { + os_free(eap->otp); + eap->otp = (u8 *) os_strdup(pos); + eap->otp_len = os_strlen(pos); + os_free(eap->pending_req_otp); + eap->pending_req_otp = NULL; + eap->pending_req_otp_len = 0; + } else if (os_strcmp(rsp, "PASSPHRASE") == 0) { + os_free(eap->private_key_passwd); + eap->private_key_passwd = (u8 *) os_strdup(pos); + eap->pending_req_passphrase = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); + return -1; + } + + return 0; +#else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); + return -1; +#endif /* IEEE8021X_EAPOL */ +} + + +static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, + const char *params, + char *buf, size_t buflen) +{ + char *pos, *end, tmp[30]; + int res, verbose, ret; + + verbose = os_strcmp(params, "-VERBOSE") == 0; + pos = buf; + end = buf + buflen; + if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(wpa_s->bssid)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + if (ssid) { + u8 *_ssid = ssid->ssid; + size_t ssid_len = ssid->ssid_len; + u8 ssid_buf[MAX_SSID_LEN]; + if (ssid_len == 0) { + int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); + if (_res < 0) + ssid_len = 0; + else + ssid_len = _res; + _ssid = ssid_buf; + } + ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", + wpa_ssid_txt(_ssid, ssid_len), + ssid->id); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (ssid->id_str) { + ret = os_snprintf(pos, end - pos, + "id_str=%s\n", + ssid->id_str); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } + + pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); + } + ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", + wpa_supplicant_state_txt(wpa_s->wpa_state)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (wpa_s->l2 && + l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { + ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, + verbose); + if (res >= 0) + pos += res; + } + + res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); + if (res >= 0) + pos += res; + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos; + int id; + struct wpa_ssid *ssid; + u8 bssid[ETH_ALEN]; + + /* cmd: "<network id> <BSSID>" */ + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos); + if (hwaddr_aton(pos, bssid)) { + wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos); + return -1; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + + os_memcpy(ssid->bssid, bssid, ETH_ALEN); + ssid->bssid_set = !is_zero_ether_addr(bssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_list_networks( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_ssid *ssid; + int ret; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "network id / ssid / bssid / flags\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ssid = wpa_s->conf->ssid; + while (ssid) { + ret = os_snprintf(pos, end - pos, "%d\t%s", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + if (ssid->bssid_set) { + ret = os_snprintf(pos, end - pos, "\t" MACSTR, + MAC2STR(ssid->bssid)); + } else { + ret = os_snprintf(pos, end - pos, "\tany"); + } + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\t%s%s", + ssid == wpa_s->current_ssid ? + "[CURRENT]" : "", + ssid->disabled ? "[DISABLED]" : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ssid = ssid->next; + } + + return pos - buf; +} + + +static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) +{ + int first = 1, ret; + ret = os_snprintf(pos, end - pos, "-"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + if (cipher & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + return pos; +} + + +static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, + const u8 *ie, size_t ie_len) +{ + struct wpa_ie_data data; + int first, ret; + + ret = os_snprintf(pos, end - pos, "[%s-", proto); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + + if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { + ret = os_snprintf(pos, end - pos, "?]"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + return pos; + } + + first = 1; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } +#ifdef CONFIG_IEEE80211R + if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sFT/EAP", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "%sFT/PSK", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } +#endif /* CONFIG_IEEE80211W */ + + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); + + if (data.capabilities & WPA_CAPABILITY_PREAUTH) { + ret = os_snprintf(pos, end - pos, "-preauth"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "]"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + + return pos; +} + +static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, + const struct wpa_scan_res *res) +{ +#ifdef CONFIG_WPS + struct wpabuf *wps_ie; + int ret; + const char *txt; + + wps_ie = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps_ie == NULL) + return pos; + + if (wps_is_selected_pbc_registrar(wps_ie)) + txt = "[WPS-PBC]"; + else if (wps_is_selected_pin_registrar(wps_ie)) + txt = "[WPS-PIN]"; + else + txt = "[WPS]"; + + ret = os_snprintf(pos, end - pos, "%s", txt); + if (ret >= 0 && ret < end - pos) + pos += ret; + wpabuf_free(wps_ie); +#endif /* CONFIG_WPS */ + + return pos; +} + + +/* Format one result on one text line into a buffer. */ +static int wpa_supplicant_ctrl_iface_scan_result( + const struct wpa_scan_res *res, char *buf, size_t buflen) +{ + char *pos, *end; + int ret; + const u8 *ie, *ie2; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", + MAC2STR(res->bssid), res->freq, res->level); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + ie = wpa_scan_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); + ie2 = wpa_scan_get_ie(res, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + pos = wpa_supplicant_wps_ie_txt(pos, end, res); + if (!ie && !ie2 && res->caps & IEEE80211_CAP_PRIVACY) { + ret = os_snprintf(pos, end - pos, "[WEP]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (res->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + ret = os_snprintf(pos, end - pos, "\t%s", + ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_scan_results( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_scan_res *res; + int ret; + size_t i; + + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) + return 0; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " + "flags / ssid\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + for (i = 0; i < wpa_s->scan_res->num; i++) { + res = wpa_s->scan_res->res[i]; + ret = wpa_supplicant_ctrl_iface_scan_result(res, pos, + end - pos); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_select_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "any" */ + if (os_strcmp(cmd, "any") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); + ssid = 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, WLAN_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>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all"); + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid == wpa_s->current_ssid && ssid->disabled) + wpa_s->reassociate = 1; + ssid->disabled = 0; + ssid = ssid->next; + } + if (wpa_s->reassociate) + wpa_supplicant_req_scan(wpa_s, 0, 0); + return 0; + } + + 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>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all"); + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = 1; + ssid = ssid->next; + } + if (wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return 0; + } + + 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, WLAN_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; + int ret; + + 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); + + ret = os_snprintf(buf, buflen, "%d\n", ssid->id); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_remove_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); + ssid = wpa_s->conf->ssid; + while (ssid) { + id = ssid->id; + ssid = ssid->next; + wpa_config_remove_network(wpa_s->conf, id); + } + if (wpa_s->current_ssid) { + eapol_sm_invalidate_cached_session(wpa_s->eapol); + wpa_supplicant_disassociate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + return 0; + } + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || + wpa_config_remove_network(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + return -1; + } + + if (ssid == wpa_s->current_ssid) { + /* + * Invalidate the EAP session cache if the current network is + * removed. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_set_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + char *name, *value; + + /* cmd: "<network id> <variable name> <value>" */ + name = os_strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + value = os_strchr(name, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'", + id, name); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) value, os_strlen(value)); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + return -1; + } + + if (wpa_config_set(ssid, name, value, 0) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " + "variable '%s'", name); + return -1; + } + + if (wpa_s->current_ssid == ssid) { + /* + * Invalidate the EAP session cache if anything in the current + * configuration changes. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + + if ((os_strcmp(name, "psk") == 0 && + value[0] == '"' && ssid->ssid_len) || + (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_get_network( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + int id; + size_t res; + struct wpa_ssid *ssid; + char *name, *value; + + /* cmd: "<network id> <variable name>" */ + name = os_strchr(cmd, ' '); + if (name == NULL || buflen == 0) + return -1; + *name++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", + id, name); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + return -1; + } + + value = wpa_config_get_no_key(ssid, name); + if (value == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + "variable '%s'", name); + return -1; + } + + res = os_strlcpy(buf, value, buflen); + if (res >= buflen) { + os_free(value); + return -1; + } + + os_free(value); + + return res; +} + + +#ifndef CONFIG_NO_CONFIG_WRITE +static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) +{ + int ret; + + if (!wpa_s->conf->update_config) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed " + "to update configuration (update_config=0)"); + return -1; + } + + ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to " + "update configuration"); + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration" + " updated"); + } + + return ret; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +static int ctrl_iface_get_capability_pairwise(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "CCMP TKIP NONE", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_group(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE " + "NONE", buflen); + if (len >= buflen) + return -1; + return len; + } + + ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { + ret = os_snprintf(pos, end - pos, " WPA-EAP"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + ret = os_snprintf(pos, end - pos, " WPA-PSK"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, " WPA-NONE"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_proto(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "RSN WPA", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { + ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_auth_alg(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { + ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { + ret = os_snprintf(pos, end - pos, "%sSHARED", + first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { + ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_get_capability( + struct wpa_supplicant *wpa_s, const char *_field, char *buf, + size_t buflen) +{ + struct wpa_driver_capa capa; + int res; + char *strict; + char field[30]; + size_t len; + + /* Determine whether or not strict checking was requested */ + len = os_strlcpy(field, _field, sizeof(field)); + if (len >= sizeof(field)) + return -1; + strict = os_strchr(field, ' '); + if (strict != NULL) { + *strict++ = '\0'; + if (os_strcmp(strict, "strict") != 0) + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s", + field, strict ? strict : ""); + + if (os_strcmp(field, "eap") == 0) { + return eap_get_names(buf, buflen); + } + + res = wpa_drv_get_capa(wpa_s, &capa); + + if (os_strcmp(field, "pairwise") == 0) + return ctrl_iface_get_capability_pairwise(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "group") == 0) + return ctrl_iface_get_capability_group(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "key_mgmt") == 0) + return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "proto") == 0) + return ctrl_iface_get_capability_proto(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "auth_alg") == 0) + return ctrl_iface_get_capability_auth_alg(res, strict, &capa, + buf, buflen); + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + +static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, + const char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN]; + size_t i; + struct wpa_scan_results *results; + struct wpa_scan_res *bss; + int ret; + char *pos, *end; + const u8 *ie, *ie2; + + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) + return 0; + + results = wpa_s->scan_res; + if (results == NULL) + return 0; + + if (hwaddr_aton(cmd, bssid) == 0) { + for (i = 0; i < results->num; i++) { + if (os_memcmp(bssid, results->res[i]->bssid, ETH_ALEN) + == 0) + break; + } + } else + i = atoi(cmd); + + if (i >= results->num || results->res[i] == NULL) + return 0; /* no match found */ + + bss = results->res[i]; + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "bssid=" MACSTR "\n" + "freq=%d\n" + "beacon_int=%d\n" + "capabilities=0x%04x\n" + "qual=%d\n" + "noise=%d\n" + "level=%d\n" + "tsf=%016llu\n" + "ie=", + MAC2STR(bss->bssid), bss->freq, bss->beacon_int, + bss->caps, bss->qual, bss->noise, bss->level, + (unsigned long long) bss->tsf); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ie = (const u8 *) (bss + 1); + for (i = 0; i < bss->ie_len; i++) { + ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "flags="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); + ie2 = wpa_scan_get_ie(bss, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + pos = wpa_supplicant_wps_ie_txt(pos, end, bss); + if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + ret = os_snprintf(pos, end - pos, "[WEP]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ret = os_snprintf(pos, end - pos, "ssid=%s\n", + ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_ap_scan( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int ap_scan = atoi(cmd); + + if (ap_scan < 0 || ap_scan > 2) + return -1; + wpa_s->conf->ap_scan = ap_scan; + return 0; +} + + +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, + char *buf, size_t *resp_len) +{ + char *reply; + const int reply_size = 2048; + int ctrl_rsp = 0; + int reply_len; + + if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || + os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + } + + reply = os_malloc(reply_size); + if (reply == NULL) { + *resp_len = 1; + return NULL; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strcmp(buf, "MIB") == 0) { + reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); + if (reply_len >= 0) { + int res; + res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (os_strncmp(buf, "STATUS", 6) == 0) { + reply_len = wpa_supplicant_ctrl_iface_status( + wpa_s, buf + 6, reply, reply_size); + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = pmksa_cache_list(wpa_s->wpa, reply, reply_size); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) + reply_len = -1; + } else if (os_strcmp(buf, "LOGON") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + } else if (os_strcmp(buf, "LOGOFF") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + } else if (os_strcmp(buf, "REASSOCIATE") == 0) { + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (os_strcmp(buf, "RECONNECT") == 0) { + if (wpa_s->disconnected) { + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +#ifdef IEEE8021X_EAPOL + } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) + reply_len = -1; +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(buf, "STKSTART ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9)) + reply_len = -1; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211R + } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) + reply_len = -1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, + reply, + reply_size); + } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) + reply_len = -1; +#endif /* CONFIG_WPS */ + } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) + { + if (wpa_supplicant_ctrl_iface_ctrl_rsp( + wpa_s, buf + os_strlen(WPA_CTRL_RSP))) + reply_len = -1; + else + ctrl_rsp = 1; + } else if (os_strcmp(buf, "RECONFIGURE") == 0) { + if (wpa_supplicant_reload_configuration(wpa_s)) + reply_len = -1; + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else if (os_strncmp(buf, "BSSID ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) + reply_len = -1; + } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_networks( + wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "DISCONNECT") == 0) { + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } else if (os_strcmp(buf, "SCAN") == 0) { + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_scan_results( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) + reply_len = -1; + } else if (os_strcmp(buf, "ADD_NETWORK") == 0) { + reply_len = wpa_supplicant_ctrl_iface_add_network( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_network( + wpa_s, buf + 12, reply, reply_size); +#ifndef CONFIG_NO_CONFIG_WRITE + } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { + if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) + reply_len = -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ + } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_capability( + wpa_s, buf + 15, reply, reply_size); + } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) + reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + wpa_s->global, reply, reply_size); + } else if (os_strcmp(buf, "INTERFACES") == 0) { + reply_len = wpa_supplicant_global_iface_interfaces( + wpa_s->global, reply, reply_size); + } else if (os_strncmp(buf, "BSS ", 4) == 0) { + reply_len = wpa_supplicant_ctrl_iface_bss( + wpa_s, buf + 4, reply, reply_size); + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + if (ctrl_rsp) + eapol_sm_notify_ctrl_response(wpa_s->eapol); + + *resp_len = reply_len; + return reply; +} + + +static int wpa_supplicant_global_iface_add(struct wpa_global *global, + char *cmd) +{ + struct wpa_interface iface; + char *pos; + + /* + * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param> + * TAB<bridge_ifname> + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); + + os_memset(&iface, 0, sizeof(iface)); + + do { + iface.ifname = pos = cmd; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.ifname[0] == '\0') + return -1; + if (pos == NULL) + break; + + iface.confname = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.confname[0] == '\0') + iface.confname = NULL; + if (pos == NULL) + break; + + iface.driver = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver[0] == '\0') + iface.driver = NULL; + if (pos == NULL) + break; + + iface.ctrl_interface = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.ctrl_interface[0] == '\0') + iface.ctrl_interface = NULL; + if (pos == NULL) + break; + + iface.driver_param = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver_param[0] == '\0') + iface.driver_param = NULL; + if (pos == NULL) + break; + + iface.bridge_ifname = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.bridge_ifname[0] == '\0') + iface.bridge_ifname = NULL; + if (pos == NULL) + break; + } while (0); + + if (wpa_supplicant_get_iface(global, iface.ifname)) + return -1; + + return wpa_supplicant_add_iface(global, &iface) ? 0 : -1; +} + + +static int wpa_supplicant_global_iface_remove(struct wpa_global *global, + char *cmd) +{ + struct wpa_supplicant *wpa_s; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); + + wpa_s = wpa_supplicant_get_iface(global, cmd); + if (wpa_s == NULL) + return -1; + return wpa_supplicant_remove_iface(global, wpa_s); +} + + +static void wpa_free_iface_info(struct wpa_interface_info *iface) +{ + struct wpa_interface_info *prev; + + while (iface) { + prev = iface; + iface = iface->next; + + os_free(prev->ifname); + os_free(prev->desc); + os_free(prev); + } +} + + +static int wpa_supplicant_global_iface_list(struct wpa_global *global, + char *buf, int len) +{ + int i, res; + struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; + char *pos, *end; + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + struct wpa_driver_ops *drv = wpa_supplicant_drivers[i]; + if (drv->get_interfaces == NULL) + continue; + tmp = drv->get_interfaces(global->drv_priv); + if (tmp == NULL) + continue; + + if (last == NULL) + iface = last = tmp; + else + last->next = tmp; + while (last->next) + last = last->next; + } + + pos = buf; + end = buf + len; + for (tmp = iface; tmp; tmp = tmp->next) { + res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", + tmp->drv_name, tmp->ifname, + tmp->desc ? tmp->desc : ""); + if (res < 0 || res >= end - pos) { + *pos = '\0'; + break; + } + pos += res; + } + + wpa_free_iface_info(iface); + + return pos - buf; +} + + +static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + char *buf, int len) +{ + int res; + char *pos, *end; + struct wpa_supplicant *wpa_s; + + wpa_s = global->ifaces; + pos = buf; + end = buf + len; + + while (wpa_s) { + res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); + if (res < 0 || res >= end - pos) { + *pos = '\0'; + break; + } + pos += res; + wpa_s = wpa_s->next; + } + return pos - buf; +} + + +char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, + char *buf, size_t *resp_len) +{ + char *reply; + const int reply_size = 2048; + int reply_len; + + wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + + reply = os_malloc(reply_size); + if (reply == NULL) { + *resp_len = 1; + return NULL; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) { + if (wpa_supplicant_global_iface_add(global, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { + if (wpa_supplicant_global_iface_remove(global, buf + 17)) + reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + global, reply, reply_size); + } else if (os_strcmp(buf, "INTERFACES") == 0) { + reply_len = wpa_supplicant_global_iface_interfaces( + global, reply, reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + *resp_len = reply_len; + return reply; +} diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h new file mode 100644 index 0000000000000..051d99a67dc60 --- /dev/null +++ b/wpa_supplicant/ctrl_iface.h @@ -0,0 +1,159 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +#ifdef CONFIG_CTRL_IFACE + +/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */ + +/** + * wpa_supplicant_ctrl_iface_process - Process ctrl_iface command + * @wpa_s: Pointer to wpa_supplicant data + * @buf: Received command buffer (nul terminated string) + * @resp_len: Variable to be set to the response length + * Returns: Response (*resp_len bytes) or %NULL on failure + * + * Control interface backends call this function when receiving a message that + * they do not process internally, i.e., anything else than ATTACH, DETACH, + * and LEVEL. The return response value is then sent to the external program + * that sent the command. Caller is responsible for freeing the buffer after + * this. If %NULL is returned, *resp_len can be set to two special values: + * 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any + * other value, no response is sent. + */ +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, + char *buf, size_t *resp_len); + +/** + * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command + * @global: Pointer to global data from wpa_supplicant_init() + * @buf: Received command buffer (nul terminated string) + * @resp_len: Variable to be set to the response length + * Returns: Response (*resp_len bytes) or %NULL on failure + * + * Control interface backends call this function when receiving a message from + * the global ctrl_iface connection. The return response value is then sent to + * the external program that sent the command. Caller is responsible for + * freeing the buffer after this. If %NULL is returned, *resp_len can be set to + * two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If + * *resp_len has any other value, no response is sent. + */ +char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, + char *buf, size_t *resp_len); + + +/* Functions that each ctrl_iface backend must implement */ + +/** + * wpa_supplicant_ctrl_iface_init - Initialize control interface + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to private data on success, %NULL on failure + * + * Initialize the control interface and start receiving commands from external + * programs. + * + * Required to be implemented in each control interface backend. + */ +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s); + +/** + * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * + * Deinitialize the control interface that was initialized with + * wpa_supplicant_ctrl_iface_init(). + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv); + +/** + * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * + * Wait until the first message from an external program using the control + * interface is received. This function can be used to delay normal startup + * processing to allow control interface programs to attach with + * %wpa_supplicant before normal operations are started. + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv); + +/** + * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: Pointer to private data on success, %NULL on failure + * + * Initialize the global control interface and start receiving commands from + * external programs. + * + * Required to be implemented in each control interface backend. + */ +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global); + +/** + * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface + * @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init() + * + * Deinitialize the global control interface that was initialized with + * wpa_supplicant_global_ctrl_iface_init(). + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_global_ctrl_iface_deinit( + struct ctrl_iface_global_priv *priv); + +#else /* CONFIG_CTRL_IFACE */ + +static inline struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + return (void *) -1; +} + +static inline void +wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level, + char *buf, size_t len) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ +} + +static inline struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + return (void *) 1; +} + +static inline void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ +} + +#endif /* CONFIG_CTRL_IFACE */ + +#endif /* CTRL_IFACE_H */ diff --git a/wpa_supplicant/ctrl_iface_dbus.c b/wpa_supplicant/ctrl_iface_dbus.c new file mode 100644 index 0000000000000..c4e329c53e45a --- /dev/null +++ b/wpa_supplicant/ctrl_iface_dbus.c @@ -0,0 +1,1115 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "wps/wps.h" +#include "ctrl_iface_dbus.h" +#include "ctrl_iface_dbus_handlers.h" + +#define _DBUS_VERSION (DBUS_VERSION_MAJOR << 8 | DBUS_VERSION_MINOR) +#define DBUS_VER(major, minor) ((major) << 8 | (minor)) + +#if _DBUS_VERSION < DBUS_VER(1,1) +#define dbus_watch_get_unix_fd dbus_watch_get_fd +#endif + + +struct ctrl_iface_dbus_priv { + DBusConnection *con; + int should_dispatch; + struct wpa_global *global; + + u32 next_objid; +}; + + +static void process_watch(struct ctrl_iface_dbus_priv *iface, + DBusWatch *watch, eloop_event_type type) +{ + dbus_connection_ref(iface->con); + + iface->should_dispatch = 0; + + if (type == EVENT_TYPE_READ) + dbus_watch_handle(watch, DBUS_WATCH_READABLE); + else if (type == EVENT_TYPE_WRITE) + dbus_watch_handle(watch, DBUS_WATCH_WRITABLE); + else if (type == EVENT_TYPE_EXCEPTION) + dbus_watch_handle(watch, DBUS_WATCH_ERROR); + + if (iface->should_dispatch) { + while (dbus_connection_get_dispatch_status(iface->con) == + DBUS_DISPATCH_DATA_REMAINS) + dbus_connection_dispatch(iface->con); + iface->should_dispatch = 0; + } + + dbus_connection_unref(iface->con); +} + + +static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION); +} + + +static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ); +} + + +static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE); +} + + +static void connection_setup_add_watch(struct ctrl_iface_dbus_priv *iface, + DBusWatch *watch) +{ + unsigned int flags; + int fd; + + if (!dbus_watch_get_enabled(watch)) + return; + + flags = dbus_watch_get_flags(watch); + fd = dbus_watch_get_unix_fd(watch); + + eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception, + iface, watch); + + if (flags & DBUS_WATCH_READABLE) { + eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read, + iface, watch); + } + if (flags & DBUS_WATCH_WRITABLE) { + eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write, + iface, watch); + } + + dbus_watch_set_data(watch, iface, NULL); +} + + +static void connection_setup_remove_watch(struct ctrl_iface_dbus_priv *iface, + DBusWatch *watch) +{ + unsigned int flags; + int fd; + + flags = dbus_watch_get_flags(watch); + fd = dbus_watch_get_unix_fd(watch); + + eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION); + + if (flags & DBUS_WATCH_READABLE) + eloop_unregister_sock(fd, EVENT_TYPE_READ); + if (flags & DBUS_WATCH_WRITABLE) + eloop_unregister_sock(fd, EVENT_TYPE_WRITE); + + dbus_watch_set_data(watch, NULL, NULL); +} + + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) +{ + connection_setup_add_watch(data, watch); + return TRUE; +} + + +static void remove_watch(DBusWatch *watch, void *data) +{ + connection_setup_remove_watch(data, watch); +} + + +static void watch_toggled(DBusWatch *watch, void *data) +{ + if (dbus_watch_get_enabled(watch)) + add_watch(watch, data); + else + remove_watch(watch, data); +} + + +static void process_timeout(void *eloop_ctx, void *sock_ctx) +{ + DBusTimeout *timeout = sock_ctx; + + dbus_timeout_handle(timeout); +} + + +static void connection_setup_add_timeout(struct ctrl_iface_dbus_priv *iface, + DBusTimeout *timeout) +{ + if (!dbus_timeout_get_enabled(timeout)) + return; + + eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000, + process_timeout, iface, timeout); + + dbus_timeout_set_data(timeout, iface, NULL); +} + + +static void connection_setup_remove_timeout(struct ctrl_iface_dbus_priv *iface, + DBusTimeout *timeout) +{ + eloop_cancel_timeout(process_timeout, iface, timeout); + dbus_timeout_set_data(timeout, NULL, NULL); +} + + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) +{ + if (!dbus_timeout_get_enabled(timeout)) + return TRUE; + + connection_setup_add_timeout(data, timeout); + + return TRUE; +} + + +static void remove_timeout(DBusTimeout *timeout, void *data) +{ + connection_setup_remove_timeout(data, timeout); +} + + +static void timeout_toggled(DBusTimeout *timeout, void *data) +{ + if (dbus_timeout_get_enabled(timeout)) + add_timeout(timeout, data); + else + remove_timeout(timeout, data); +} + + +static void process_wakeup_main(int sig, void *eloop_ctx, void *signal_ctx) +{ + struct ctrl_iface_dbus_priv *iface = signal_ctx; + + if (sig != SIGPOLL || !iface->con) + return; + + if (dbus_connection_get_dispatch_status(iface->con) != + DBUS_DISPATCH_DATA_REMAINS) + return; + + /* Only dispatch once - we do not want to starve other events */ + dbus_connection_ref(iface->con); + dbus_connection_dispatch(iface->con); + dbus_connection_unref(iface->con); +} + + +/** + * wakeup_main - Attempt to wake our mainloop up + * @data: dbus control interface private data + * + * Try to wake up the main eloop so it will process + * dbus events that may have happened. + */ +static void wakeup_main(void *data) +{ + struct ctrl_iface_dbus_priv *iface = data; + + /* Use SIGPOLL to break out of the eloop select() */ + raise(SIGPOLL); + iface->should_dispatch = 1; +} + + +/** + * connection_setup_wakeup_main - Tell dbus about our wakeup_main function + * @iface: dbus control interface private data + * Returns: 0 on success, -1 on failure + * + * Register our wakeup_main handler with dbus + */ +static int connection_setup_wakeup_main(struct ctrl_iface_dbus_priv *iface) +{ + if (eloop_register_signal(SIGPOLL, process_wakeup_main, iface)) + return -1; + + dbus_connection_set_wakeup_main_function(iface->con, wakeup_main, + iface, NULL); + + return 0; +} + + +/** + * wpa_supplicant_dbus_next_objid - Return next available object id + * @iface: dbus control interface private data + * Returns: Object id + */ +u32 wpa_supplicant_dbus_next_objid (struct ctrl_iface_dbus_priv *iface) +{ + return iface->next_objid++; +} + + +/** + * wpas_dbus_decompose_object_path - Decompose an interface object path into parts + * @path: The dbus object path + * @network: (out) the configured network this object path refers to, if any + * @bssid: (out) the scanned bssid this object path refers to, if any + * Returns: The object path of the network interface this path refers to + * + * For a given object path, decomposes the object path into object id, network, + * and BSSID parts, if those parts exist. + */ +char * wpas_dbus_decompose_object_path(const char *path, char **network, + char **bssid) +{ + const unsigned int dev_path_prefix_len = + strlen(WPAS_DBUS_PATH_INTERFACES "/"); + char *obj_path_only; + char *next_sep; + + /* Be a bit paranoid about path */ + if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/", + dev_path_prefix_len)) + return NULL; + + /* Ensure there's something at the end of the path */ + if ((path + dev_path_prefix_len)[0] == '\0') + return NULL; + + obj_path_only = strdup(path); + if (obj_path_only == NULL) + return NULL; + + next_sep = strchr(obj_path_only + dev_path_prefix_len, '/'); + if (next_sep != NULL) { + const char *net_part = strstr(next_sep, + WPAS_DBUS_NETWORKS_PART "/"); + const char *bssid_part = strstr(next_sep, + WPAS_DBUS_BSSIDS_PART "/"); + + if (network && net_part) { + /* Deal with a request for a configured network */ + const char *net_name = net_part + + strlen(WPAS_DBUS_NETWORKS_PART "/"); + *network = NULL; + if (strlen(net_name)) + *network = strdup(net_name); + } else if (bssid && bssid_part) { + /* Deal with a request for a scanned BSSID */ + const char *bssid_name = bssid_part + + strlen(WPAS_DBUS_BSSIDS_PART "/"); + if (strlen(bssid_name)) + *bssid = strdup(bssid_name); + else + *bssid = NULL; + } + + /* Cut off interface object path before "/" */ + *next_sep = '\0'; + } + + return obj_path_only; +} + + +/** + * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: A dbus error message + * + * Convenience function to create and return an invalid interface error + */ +DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE, + "wpa_supplicant knows nothing about " + "this interface."); +} + + +/** + * wpas_dbus_new_invalid_network_error - Return a new invalid network error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid network error + */ +DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK, + "The requested network does not exist."); +} + + +/** + * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid bssid error + */ +static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID, + "The BSSID requested was invalid."); +} + + +/** + * wpas_dispatch_network_method - dispatch messages for configured networks + * @message: the incoming dbus message + * @wpa_s: a network interface's data + * @network_id: id of the configured network we're interested in + * Returns: a reply dbus message, or a dbus error message + * + * This function dispatches all incoming dbus messages for configured networks. + */ +static DBusMessage * wpas_dispatch_network_method(DBusMessage *message, + struct wpa_supplicant *wpa_s, + int network_id) +{ + DBusMessage *reply = NULL; + const char *method = dbus_message_get_member(message); + struct wpa_ssid *ssid; + + ssid = wpa_config_get_network(wpa_s->conf, network_id); + if (ssid == NULL) + return wpas_dbus_new_invalid_network_error(message); + + if (!strcmp(method, "set")) + reply = wpas_dbus_iface_set_network(message, wpa_s, ssid); + else if (!strcmp(method, "enable")) + reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid); + else if (!strcmp(method, "disable")) + reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid); + + return reply; +} + + +/** + * wpas_dispatch_bssid_method - dispatch messages for scanned networks + * @message: the incoming dbus message + * @wpa_s: a network interface's data + * @bssid: bssid of the scanned network we're interested in + * Returns: a reply dbus message, or a dbus error message + * + * This function dispatches all incoming dbus messages for scanned networks. + */ +static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message, + struct wpa_supplicant *wpa_s, + const char *bssid) +{ + DBusMessage *reply = NULL; + const char *method = dbus_message_get_member(message); + struct wpa_scan_res *res = NULL; + size_t i; + + /* Ensure we actually have scan data */ + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) { + reply = wpas_dbus_new_invalid_bssid_error(message); + goto out; + } + + /* Find the bssid's scan data */ + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *search_res = wpa_s->scan_res->res[i]; + char mac_str[18]; + + memset(mac_str, 0, sizeof(mac_str)); + snprintf(mac_str, sizeof(mac_str) - 1, WPAS_DBUS_BSSID_FORMAT, + MAC2STR(search_res->bssid)); + if (!strcmp(bssid, mac_str)) { + res = search_res; + break; + } + } + + if (!res) { + reply = wpas_dbus_new_invalid_bssid_error(message); + goto out; + } + + /* Dispatch the method call against the scanned bssid */ + if (!strcmp(method, "properties")) + reply = wpas_dbus_bssid_properties(message, wpa_s, res); + +out: + return reply; +} + + +/** + * wpas_iface_message_handler - Dispatch messages for interfaces or networks + * @connection: Connection to the system message bus + * @message: An incoming dbus message + * @user_data: A pointer to a dbus control interface data structure + * Returns: Whether or not the message was handled + * + * This function dispatches all incoming dbus messages for network interfaces, + * or objects owned by them, such as scanned BSSIDs and configured networks. + */ +static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *method = dbus_message_get_member(message); + const char *path = dbus_message_get_path(message); + const char *msg_interface = dbus_message_get_interface(message); + char *iface_obj_path = NULL; + char *network = NULL; + char *bssid = NULL; + DBusMessage *reply = NULL; + + /* Caller must specify a message interface */ + if (!msg_interface) + goto out; + + iface_obj_path = wpas_dbus_decompose_object_path(path, &network, + &bssid); + if (iface_obj_path == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + /* Make sure the message's object path actually refers to the + * wpa_supplicant structure it's supposed to (which is wpa_s) + */ + if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, + iface_obj_path) != wpa_s) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { + /* A method for one of this interface's configured networks */ + int nid = strtoul(network, NULL, 10); + if (errno != EINVAL) + reply = wpas_dispatch_network_method(message, wpa_s, + nid); + else + reply = wpas_dbus_new_invalid_network_error(message); + } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) { + /* A method for one of this interface's scanned BSSIDs */ + reply = wpas_dispatch_bssid_method(message, wpa_s, bssid); + } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) { + /* A method for an interface only. */ + if (!strcmp(method, "scan")) + reply = wpas_dbus_iface_scan(message, wpa_s); + else if (!strcmp(method, "scanResults")) + reply = wpas_dbus_iface_scan_results(message, wpa_s); + else if (!strcmp(method, "addNetwork")) + reply = wpas_dbus_iface_add_network(message, wpa_s); + else if (!strcmp(method, "removeNetwork")) + reply = wpas_dbus_iface_remove_network(message, wpa_s); + else if (!strcmp(method, "selectNetwork")) + reply = wpas_dbus_iface_select_network(message, wpa_s); + else if (!strcmp(method, "capabilities")) + reply = wpas_dbus_iface_capabilities(message, wpa_s); + else if (!strcmp(method, "disconnect")) + reply = wpas_dbus_iface_disconnect(message, wpa_s); + else if (!strcmp(method, "setAPScan")) + reply = wpas_dbus_iface_set_ap_scan(message, wpa_s); + else if (!strcmp(method, "setSmartcardModules")) + reply = wpas_dbus_iface_set_smartcard_modules(message, + wpa_s); + else if (!strcmp(method, "state")) + reply = wpas_dbus_iface_get_state(message, wpa_s); + else if (!strcmp(method, "setBlobs")) + reply = wpas_dbus_iface_set_blobs(message, wpa_s); + else if (!strcmp(method, "removeBlobs")) + reply = wpas_dbus_iface_remove_blobs(message, wpa_s); + } + + /* If the message was handled, send back the reply */ + if (reply) { + if (!dbus_message_get_no_reply(message)) + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + +out: + free(iface_obj_path); + free(network); + free(bssid); + return reply ? DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +/** + * wpas_message_handler - dispatch incoming dbus messages + * @connection: connection to the system message bus + * @message: an incoming dbus message + * @user_data: a pointer to a dbus control interface data structure + * Returns: whether or not the message was handled + * + * This function dispatches all incoming dbus messages to the correct + * handlers, depending on what the message's target object path is, + * and what the method call is. + */ +static DBusHandlerResult wpas_message_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct ctrl_iface_dbus_priv *ctrl_iface = user_data; + const char *method; + const char *path; + const char *msg_interface; + DBusMessage *reply = NULL; + + method = dbus_message_get_member(message); + path = dbus_message_get_path(message); + msg_interface = dbus_message_get_interface(message); + if (!method || !path || !ctrl_iface || !msg_interface) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + /* Validate the method interface */ + if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (!strcmp(path, WPAS_DBUS_PATH)) { + /* dispatch methods against our global dbus interface here */ + if (!strcmp(method, "addInterface")) { + reply = wpas_dbus_global_add_interface( + message, ctrl_iface->global); + } else if (!strcmp(method, "removeInterface")) { + reply = wpas_dbus_global_remove_interface( + message, ctrl_iface->global); + } else if (!strcmp(method, "getInterface")) { + reply = wpas_dbus_global_get_interface( + message, ctrl_iface->global); + } + } + + /* If the message was handled, send back the reply */ + if (reply) { + if (!dbus_message_get_no_reply(message)) + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + return reply ? DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +/** + * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal + * @wpa_s: %wpa_supplicant network interface data + * Returns: 0 on success, -1 on failure + * + * Notify listeners that this interface has updated scan results. + */ +void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_dbus_priv *iface = wpa_s->global->dbus_ctrl_iface; + DBusMessage *_signal; + const char *path; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + path = wpa_supplicant_get_dbus_path(wpa_s); + if (path == NULL) { + perror("wpa_supplicant_dbus_notify_scan_results[dbus]: " + "interface didn't have a dbus path"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_scan_results[dbus]: " + "interface didn't have a dbus path; can't send " + "scan result signal."); + return; + } + _signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, + "ScanResultsAvailable"); + if (_signal == NULL) { + perror("wpa_supplicant_dbus_notify_scan_results[dbus]: " + "couldn't create dbus signal; likely out of memory"); + wpa_printf(MSG_ERROR, "dbus control interface: not enough " + "memory to send scan results signal."); + return; + } + dbus_connection_send(iface->con, _signal, NULL); + dbus_message_unref(_signal); +} + + +/** + * wpa_supplicant_dbus_notify_state_change - Send a state change signal + * @wpa_s: %wpa_supplicant network interface data + * @new_state: new state wpa_supplicant is entering + * @old_state: old state wpa_supplicant is leaving + * Returns: 0 on success, -1 on failure + * + * Notify listeners that wpa_supplicant has changed state + */ +void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + wpa_states new_state, + wpa_states old_state) +{ + struct ctrl_iface_dbus_priv *iface; + DBusMessage *_signal = NULL; + const char *path; + const char *new_state_str, *old_state_str; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL) + return; + iface = wpa_s->global->dbus_ctrl_iface; + if (iface == NULL) + return; + + /* Only send signal if state really changed */ + if (new_state == old_state) + return; + + path = wpa_supplicant_get_dbus_path(wpa_s); + if (path == NULL) { + perror("wpa_supplicant_dbus_notify_state_change[dbus]: " + "interface didn't have a dbus path"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_state_change[dbus]: " + "interface didn't have a dbus path; can't send " + "signal."); + return; + } + _signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, + "StateChange"); + if (_signal == NULL) { + perror("wpa_supplicant_dbus_notify_state_change[dbus]: " + "couldn't create dbus signal; likely out of memory"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_state_change[dbus]: " + "couldn't create dbus signal; likely out of " + "memory."); + return; + } + + new_state_str = wpa_supplicant_state_txt(new_state); + old_state_str = wpa_supplicant_state_txt(old_state); + if (new_state_str == NULL || old_state_str == NULL) { + perror("wpa_supplicant_dbus_notify_state_change[dbus]: " + "couldn't convert state strings"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_state_change[dbus]: " + "couldn't convert state strings."); + goto out; + } + + if (!dbus_message_append_args(_signal, + DBUS_TYPE_STRING, &new_state_str, + DBUS_TYPE_STRING, &old_state_str, + DBUS_TYPE_INVALID)) { + perror("wpa_supplicant_dbus_notify_state_change[dbus]: " + "not enough memory to construct state change signal."); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_state_change[dbus]: " + "not enough memory to construct state change " + "signal."); + goto out; + } + + dbus_connection_send(iface->con, _signal, NULL); + +out: + dbus_message_unref(_signal); +} + + +#ifdef CONFIG_WPS +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ + struct ctrl_iface_dbus_priv *iface; + DBusMessage *_signal = NULL; + const char *path; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL) + return; + iface = wpa_s->global->dbus_ctrl_iface; + if (iface == NULL) + return; + + path = wpa_supplicant_get_dbus_path(wpa_s); + if (path == NULL) { + perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "interface didn't have a dbus path"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "interface didn't have a dbus path; can't send " + "signal."); + return; + } + _signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, + "WpsCred"); + if (_signal == NULL) { + perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "couldn't create dbus signal; likely out of memory"); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "couldn't create dbus signal; likely out of " + "memory."); + return; + } + + if (!dbus_message_append_args(_signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &cred->cred_attr, cred->cred_attr_len, + DBUS_TYPE_INVALID)) { + perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "not enough memory to construct signal."); + wpa_printf(MSG_ERROR, + "wpa_supplicant_dbus_notify_wps_cred[dbus]: " + "not enough memory to construct signal."); + goto out; + } + + dbus_connection_send(iface->con, _signal, NULL); + +out: + dbus_message_unref(_signal); +} +#endif /* CONFIG_WPS */ + + +/** + * integrate_with_eloop - Register our mainloop integration with dbus + * @connection: connection to the system message bus + * @iface: a dbus control interface data structure + * Returns: 0 on success, -1 on failure + * + * We register our mainloop integration functions with dbus here. + */ +static int integrate_with_eloop(DBusConnection *connection, + struct ctrl_iface_dbus_priv *iface) +{ + if (!dbus_connection_set_watch_functions(connection, add_watch, + remove_watch, watch_toggled, + iface, NULL)) { + perror("dbus_connection_set_watch_functions[dbus]"); + wpa_printf(MSG_ERROR, "Not enough memory to set up dbus."); + return -1; + } + + if (!dbus_connection_set_timeout_functions(connection, add_timeout, + remove_timeout, + timeout_toggled, iface, + NULL)) { + perror("dbus_connection_set_timeout_functions[dbus]"); + wpa_printf(MSG_ERROR, "Not enough memory to set up dbus."); + return -1; + } + + if (connection_setup_wakeup_main(iface) < 0) { + perror("connection_setup_wakeup_main[dbus]"); + wpa_printf(MSG_ERROR, "Could not setup main wakeup function."); + return -1; + } + + return 0; +} + + +/** + * dispatch_initial_dbus_messages - Dispatch initial dbus messages after + * claiming bus name + * @eloop_ctx: the DBusConnection to dispatch on + * @timeout_ctx: unused + * + * If clients are quick to notice that wpa_supplicant claimed its bus name, + * there may have been messages that came in before initialization was + * all finished. Dispatch those here. + */ +static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx) +{ + DBusConnection *con = eloop_ctx; + + while (dbus_connection_get_dispatch_status(con) == + DBUS_DISPATCH_DATA_REMAINS) + dbus_connection_dispatch(con); +} + + +/** + * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: Pointer to dbus_ctrl_iface date or %NULL on failure + * + * Initialize the dbus control interface and start receiving commands from + * external programs over the bus. + */ +struct ctrl_iface_dbus_priv * +wpa_supplicant_dbus_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_dbus_priv *iface; + DBusError error; + int ret = -1; + DBusObjectPathVTable wpas_vtable = { + NULL, &wpas_message_handler, NULL, NULL, NULL, NULL + }; + + iface = os_zalloc(sizeof(struct ctrl_iface_dbus_priv)); + if (iface == NULL) + return NULL; + + iface->global = global; + + /* Get a reference to the system bus */ + dbus_error_init(&error); + iface->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + dbus_error_free(&error); + if (!iface->con) { + perror("dbus_bus_get[ctrl_iface_dbus]"); + wpa_printf(MSG_ERROR, "Could not acquire the system bus."); + goto fail; + } + + /* Tell dbus about our mainloop integration functions */ + if (integrate_with_eloop(iface->con, iface)) + goto fail; + + /* Register the message handler for the global dbus interface */ + if (!dbus_connection_register_object_path(iface->con, + WPAS_DBUS_PATH, &wpas_vtable, + iface)) { + perror("dbus_connection_register_object_path[dbus]"); + wpa_printf(MSG_ERROR, "Could not set up DBus message " + "handler."); + goto fail; + } + + /* Register our service with the message bus */ + dbus_error_init(&error); + switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE, + 0, &error)) { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + ret = 0; + break; + case DBUS_REQUEST_NAME_REPLY_EXISTS: + case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + perror("dbus_bus_request_name[dbus]"); + wpa_printf(MSG_ERROR, "Could not request DBus service name: " + "already registered."); + break; + default: + perror("dbus_bus_request_name[dbus]"); + wpa_printf(MSG_ERROR, "Could not request DBus service name: " + "%s %s.", error.name, error.message); + break; + } + dbus_error_free(&error); + + if (ret != 0) + goto fail; + + wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE + "'."); + + /* + * Dispatch initial DBus messages that may have come in since the bus + * name was claimed above. Happens when clients are quick to notice the + * wpa_supplicant service. + * + * FIXME: is there a better solution to this problem? + */ + eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, + iface->con, NULL); + + return iface; + +fail: + wpa_supplicant_dbus_ctrl_iface_deinit(iface); + return NULL; +} + + +/** + * wpa_supplicant_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface + * @iface: Pointer to dbus private data from + * wpa_supplicant_dbus_ctrl_iface_init() + * + * Deinitialize the dbus control interface that was initialized with + * wpa_supplicant_dbus_ctrl_iface_init(). + */ +void wpa_supplicant_dbus_ctrl_iface_deinit(struct ctrl_iface_dbus_priv *iface) +{ + if (iface == NULL) + return; + + if (iface->con) { + eloop_cancel_timeout(dispatch_initial_dbus_messages, + iface->con, NULL); + dbus_connection_set_watch_functions(iface->con, NULL, NULL, + NULL, NULL, NULL); + dbus_connection_set_timeout_functions(iface->con, NULL, NULL, + NULL, NULL, NULL); + dbus_connection_unref(iface->con); + } + + memset(iface, 0, sizeof(struct ctrl_iface_dbus_priv)); + free(iface); +} + + +/** + * wpas_dbus_register_new_iface - Register a new interface with dbus + * @wpa_s: %wpa_supplicant interface description structure to register + * Returns: 0 on success, -1 on error + * + * Registers a new interface with dbus and assigns it a dbus object path. + */ +int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_dbus_priv *ctrl_iface = + wpa_s->global->dbus_ctrl_iface; + DBusConnection * con; + u32 next; + DBusObjectPathVTable vtable = { + NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL + }; + char *path; + int ret = -1; + + /* Do nothing if the control interface is not turned on */ + if (ctrl_iface == NULL) + return 0; + + con = ctrl_iface->con; + next = wpa_supplicant_dbus_next_objid(ctrl_iface); + + /* Create and set the interface's object path */ + path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (path == NULL) + return -1; + snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + WPAS_DBUS_PATH_INTERFACES "/%u", + next); + if (wpa_supplicant_set_dbus_path(wpa_s, path)) { + wpa_printf(MSG_DEBUG, + "Failed to set dbus path for interface %s", + wpa_s->ifname); + goto out; + } + + /* Register the message handler for the interface functions */ + if (!dbus_connection_register_fallback(con, path, &vtable, wpa_s)) { + perror("wpas_dbus_register_iface [dbus]"); + wpa_printf(MSG_ERROR, "Could not set up DBus message " + "handler for interface %s.", wpa_s->ifname); + goto out; + } + ret = 0; + +out: + free(path); + return ret; +} + + +/** + * wpas_dbus_unregister_iface - Unregister an interface from dbus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + * + * Unregisters the interface with dbus + */ +int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_dbus_priv *ctrl_iface; + DBusConnection *con; + const char *path; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus_ctrl_iface; + if (ctrl_iface == NULL) + return 0; + + con = ctrl_iface->con; + path = wpa_supplicant_get_dbus_path(wpa_s); + + if (!dbus_connection_unregister_object_path(con, path)) + return -1; + + free(wpa_s->dbus_path); + wpa_s->dbus_path = NULL; + + return 0; +} + + +/** + * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @path: Pointer to a dbus object path representing an interface + * Returns: Pointer to the interface or %NULL if not found + */ +struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( + struct wpa_global *global, const char *path) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (strcmp(wpa_s->dbus_path, path) == 0) + return wpa_s; + } + return NULL; +} + + +/** + * wpa_supplicant_set_dbus_path - Assign a dbus path to an interface + * @wpa_s: wpa_supplicant interface structure + * @path: dbus path to set on the interface + * Returns: 0 on succes, -1 on error + */ +int wpa_supplicant_set_dbus_path(struct wpa_supplicant *wpa_s, + const char *path) +{ + u32 len = strlen (path); + if (len >= WPAS_DBUS_OBJECT_PATH_MAX) + return -1; + if (wpa_s->dbus_path) + return -1; + wpa_s->dbus_path = strdup(path); + return 0; +} + + +/** + * wpa_supplicant_get_dbus_path - Get an interface's dbus path + * @wpa_s: %wpa_supplicant interface structure + * Returns: Interface's dbus object path, or %NULL on error + */ +const char * wpa_supplicant_get_dbus_path(struct wpa_supplicant *wpa_s) +{ + return wpa_s->dbus_path; +} diff --git a/wpa_supplicant/ctrl_iface_dbus.h b/wpa_supplicant/ctrl_iface_dbus.h new file mode 100644 index 0000000000000..68919de0235e1 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_dbus.h @@ -0,0 +1,156 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CTRL_IFACE_DBUS_H +#define CTRL_IFACE_DBUS_H + +struct wps_credential; + +#ifdef CONFIG_CTRL_IFACE_DBUS + +#ifndef SIGPOLL +#ifdef SIGIO +/* + * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for + * FreeBSD. + */ +#define SIGPOLL SIGIO +#endif +#endif + +#include <dbus/dbus.h> + +#define WPAS_DBUS_OBJECT_PATH_MAX 150 + +#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant" +#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant" +#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant" + +#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" + +#define WPAS_DBUS_NETWORKS_PART "Networks" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" + +#define WPAS_DBUS_BSSIDS_PART "BSSIDs" +#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID" + + +/* Errors */ +#define WPAS_ERROR_INVALID_NETWORK \ + WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork" +#define WPAS_ERROR_INVALID_BSSID \ + WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID" + +#define WPAS_ERROR_INVALID_OPTS \ + WPAS_DBUS_INTERFACE ".InvalidOptions" +#define WPAS_ERROR_INVALID_IFACE \ + WPAS_DBUS_INTERFACE ".InvalidInterface" + +#define WPAS_ERROR_ADD_ERROR \ + WPAS_DBUS_INTERFACE ".AddError" +#define WPAS_ERROR_EXISTS_ERROR \ + WPAS_DBUS_INTERFACE ".ExistsError" +#define WPAS_ERROR_REMOVE_ERROR \ + WPAS_DBUS_INTERFACE ".RemoveError" + +#define WPAS_ERROR_SCAN_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".ScanError" +#define WPAS_ERROR_ADD_NETWORK_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError" +#define WPAS_ERROR_INTERNAL_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".InternalError" +#define WPAS_ERROR_REMOVE_NETWORK_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError" + +#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x" + +struct wpa_global; +struct wpa_supplicant; + +struct ctrl_iface_dbus_priv * +wpa_supplicant_dbus_ctrl_iface_init(struct wpa_global *global); +void wpa_supplicant_dbus_ctrl_iface_deinit(struct ctrl_iface_dbus_priv *iface); +void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s); +void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + wpa_states new_state, + wpa_states old_state); +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred); + +char * wpas_dbus_decompose_object_path(const char *path, char **network, + char **bssid); + +int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); +int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); + + +/* Methods internal to the dbus control interface */ +u32 wpa_supplicant_dbus_next_objid(struct ctrl_iface_dbus_priv *iface); + +int wpa_supplicant_set_dbus_path(struct wpa_supplicant *wpa_s, + const char *path); +const char *wpa_supplicant_get_dbus_path(struct wpa_supplicant *wpa_s); +struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( + struct wpa_global *global, const char *path); + +DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message); +DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message); + +#else /* CONFIG_CTRL_IFACE_DBUS */ + +static inline struct ctrl_iface_dbus_priv * +wpa_supplicant_dbus_ctrl_iface_init(struct wpa_global *global) +{ + return (struct ctrl_iface_dbus_priv *) 1; +} + +static inline void +wpa_supplicant_dbus_ctrl_iface_deinit(struct ctrl_iface_dbus_priv *iface) +{ +} + +static inline void +wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + wpa_states new_state, + wpa_states old_state) +{ +} + +static inline void +wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +} + +static inline int +wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline int +wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +#endif /* CONFIG_CTRL_IFACE_DBUS */ + +#endif /* CTRL_IFACE_DBUS_H */ diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.c b/wpa_supplicant/ctrl_iface_dbus_handlers.c new file mode 100644 index 0000000000000..3c298043b3f77 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_dbus_handlers.c @@ -0,0 +1,1410 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface_dbus.h" +#include "ctrl_iface_dbus_handlers.h" +#include "eap_peer/eap_methods.h" +#include "dbus_dict_helpers.h" +#include "ieee802_11_defs.h" +#include "wpas_glue.h" +#include "eapol_supp/eapol_supp_sm.h" + + +/** + * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid options error + */ +static DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, + const char *arg) +{ + DBusMessage *reply; + + reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, + "Did not receive correct message " + "arguments."); + if (arg != NULL) + dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + + return reply; +} + + +/** + * wpas_dbus_new_success_reply - Return a new success reply message + * @message: Pointer to incoming dbus message this reply refers to + * Returns: a dbus message containing a single UINT32 that indicates + * success (ie, a value of 1) + * + * Convenience function to create and return a success reply message + */ +static DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) +{ + DBusMessage *reply; + unsigned int success = 1; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, + DBUS_TYPE_INVALID); + return reply; +} + + +static void wpas_dbus_free_wpa_interface(struct wpa_interface *iface) +{ + free((char *) iface->driver); + free((char *) iface->driver_param); + free((char *) iface->confname); + free((char *) iface->bridge_ifname); +} + + +/** + * wpas_dbus_global_add_interface - Request registration of a network interface + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the new interface object, + * or a dbus error message with more information + * + * Handler function for "addInterface" method call. Handles requests + * by dbus clients to register a network interface that wpa_supplicant + * will manage. + */ +DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_interface iface; + char *ifname = NULL; + DBusMessage *reply = NULL; + DBusMessageIter iter; + + memset(&iface, 0, sizeof(iface)); + + dbus_message_iter_init(message, &iter); + + /* First argument: interface name (DBUS_TYPE_STRING) + * Required; must be non-zero length + */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto error; + dbus_message_iter_get_basic(&iter, &ifname); + if (!strlen(ifname)) + goto error; + iface.ifname = ifname; + + /* Second argument: dict of options */ + if (dbus_message_iter_next(&iter)) { + DBusMessageIter iter_dict; + struct wpa_dbus_dict_entry entry; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + goto error; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!strcmp(entry.key, "driver") && + (entry.type == DBUS_TYPE_STRING)) { + iface.driver = strdup(entry.str_value); + if (iface.driver == NULL) + goto error; + } else if (!strcmp(entry.key, "driver-params") && + (entry.type == DBUS_TYPE_STRING)) { + iface.driver_param = strdup(entry.str_value); + if (iface.driver_param == NULL) + goto error; + } else if (!strcmp(entry.key, "config-file") && + (entry.type == DBUS_TYPE_STRING)) { + iface.confname = strdup(entry.str_value); + if (iface.confname == NULL) + goto error; + } else if (!strcmp(entry.key, "bridge-ifname") && + (entry.type == DBUS_TYPE_STRING)) { + iface.bridge_ifname = strdup(entry.str_value); + if (iface.bridge_ifname == NULL) + goto error; + } else { + wpa_dbus_dict_entry_clear(&entry); + goto error; + } + wpa_dbus_dict_entry_clear(&entry); + } + } + + /* + * Try to get the wpa_supplicant record for this iface, return + * an error if we already control it. + */ + if (wpa_supplicant_get_iface(global, iface.ifname) != NULL) { + reply = dbus_message_new_error(message, + WPAS_ERROR_EXISTS_ERROR, + "wpa_supplicant already " + "controls this interface."); + } else { + struct wpa_supplicant *wpa_s; + /* Otherwise, have wpa_supplicant attach to it. */ + if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + const char *path = wpa_supplicant_get_dbus_path(wpa_s); + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID); + } else { + reply = dbus_message_new_error(message, + WPAS_ERROR_ADD_ERROR, + "wpa_supplicant " + "couldn't grab this " + "interface."); + } + } + wpas_dbus_free_wpa_interface(&iface); + return reply; + +error: + wpas_dbus_free_wpa_interface(&iface); + return wpas_dbus_new_invalid_opts_error(message, NULL); +} + + +/** + * wpas_dbus_global_remove_interface - Request deregistration of an interface + * @message: Pointer to incoming dbus message + * @global: wpa_supplicant global data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "removeInterface" method call. Handles requests + * by dbus clients to deregister a network interface that wpa_supplicant + * currently manages. + */ +DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + char *path; + DBusMessage *reply = NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); + if (wpa_s == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + if (!wpa_supplicant_remove_iface(global, wpa_s)) { + reply = wpas_dbus_new_success_reply(message); + } else { + reply = dbus_message_new_error(message, + WPAS_ERROR_REMOVE_ERROR, + "wpa_supplicant couldn't " + "remove this interface."); + } + +out: + return reply; +} + + +/** + * wpas_dbus_global_get_interface - Get the object path for an interface name + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the interface object, + * or a dbus error message with more information + * + * Handler function for "getInterface" method call. Handles requests + * by dbus clients for the object path of an specific network interface. + */ +DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + const char *ifname; + const char *path; + struct wpa_supplicant *wpa_s; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &ifname, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + path = wpa_supplicant_get_dbus_path(wpa_s); + if (path == NULL) { + reply = dbus_message_new_error(message, + WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred " + "getting the interface."); + goto out; + } + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_scan - Request a wireless scan on an interface + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "scan" method call of a network device. Requests + * that wpa_supplicant perform a wireless scan as soon as possible + * on a particular wireless interface. + */ +DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_scan_results - Get the results of a recent scan request + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing a dbus array of objects paths, or returns + * a dbus error message if not scan results could be found + * + * Handler function for "scanResults" method call of a network device. Returns + * a dbus message containing the object paths of wireless networks found. + */ +DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter; + DBusMessageIter sub_iter; + size_t i; + + /* Ensure we've actually got scan results to return */ + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) { + reply = dbus_message_new_error(message, WPAS_ERROR_SCAN_ERROR, + "An error ocurred getting scan " + "results."); + goto out; + } + + /* Create and initialize the return message */ + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH_AS_STRING, + &sub_iter); + + /* Loop through scan results and append each result's object path */ + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *res = wpa_s->scan_res->res[i]; + char *path; + + path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (path == NULL) { + perror("wpas_dbus_iface_scan_results[dbus]: out of " + "memory."); + wpa_printf(MSG_ERROR, "dbus control interface: not " + "enough memory to send scan results " + "signal."); + break; + } + /* Construct the object path for this network. Note that ':' + * is not a valid character in dbus object paths. + */ + snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_BSSIDS_PART "/" + WPAS_DBUS_BSSID_FORMAT, + wpa_supplicant_get_dbus_path(wpa_s), + MAC2STR(res->bssid)); + dbus_message_iter_append_basic(&sub_iter, + DBUS_TYPE_OBJECT_PATH, &path); + free(path); + } + + dbus_message_iter_close_container(&iter, &sub_iter); + +out: + return reply; +} + + +/** + * wpas_dbus_bssid_properties - Return the properties of a scanned network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @res: wpa_supplicant scan result for which to get properties + * Returns: a dbus message containing the properties for the requested network + * + * Handler function for "properties" method call of a scanned network. + * Returns a dbus message containing the the properties. + */ +DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, iter_dict; + const u8 *ie; + + /* Dump the properties into a dbus message */ + reply = dbus_message_new_method_return(message); + + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto error; + + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", + (const char *) res->bssid, + ETH_ALEN)) + goto error; + + ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", + (const char *) (ie + 2), + ie[1])) + goto error; + } + + ie = wpa_scan_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + ie = wpa_scan_get_ie(res, WLAN_EID_RSN); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + ie = wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + if (res->freq) { + if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", + res->freq)) + goto error; + } + if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", + res->caps)) + goto error; + if (!wpa_dbus_dict_append_int32(&iter_dict, "quality", res->qual)) + goto error; + if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", res->noise)) + goto error; + if (!wpa_dbus_dict_append_int32(&iter_dict, "level", res->level)) + goto error; + if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", + wpa_scan_get_max_rate(res) * 500000)) + goto error; + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto error; + + return reply; + +error: + if (reply) + dbus_message_unref(reply); + return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning " + "BSSID properties."); +} + + +/** + * wpas_dbus_iface_capabilities - Return interface capabilities + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a dict of strings + * + * Handler function for "capabilities" method call of an interface. + */ +DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_driver_capa capa; + int res; + DBusMessageIter iter, iter_dict; + char **eap_methods; + size_t num_items; + dbus_bool_t strict = FALSE; + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_BOOLEAN, &strict, + DBUS_TYPE_INVALID)) + strict = FALSE; + + reply = dbus_message_new_method_return(message); + + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto error; + + /* EAP methods */ + eap_methods = eap_get_names_as_string_array(&num_items); + if (eap_methods) { + dbus_bool_t success = FALSE; + size_t i = 0; + + success = wpa_dbus_dict_append_string_array( + &iter_dict, "eap", (const char **) eap_methods, + num_items); + + /* free returned method array */ + while (eap_methods[i]) + free(eap_methods[i++]); + free(eap_methods); + + if (!success) + goto error; + } + + res = wpa_drv_get_capa(wpa_s, &capa); + + /***** pairwise cipher */ + if (res < 0) { + if (!strict) { + const char *args[] = {"CCMP", "TKIP", "NONE"}; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "pairwise", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) + goto error; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "NONE")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** group cipher */ + if (res < 0) { + if (!strict) { + const char *args[] = { + "CCMP", "TKIP", "WEP104", "WEP40" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "group", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP104")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP40")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** key management */ + if (res < 0) { + if (!strict) { + const char *args[] = { + "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", + "NONE" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "key_mgmt", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "NONE")) + goto error; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "IEEE8021X")) + goto error; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-EAP")) + goto error; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-PSK")) + goto error; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-NONE")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** WPA protocol */ + if (res < 0) { + if (!strict) { + const char *args[] = { "RSN", "WPA" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "proto", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "RSN")) + goto error; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** auth alg */ + if (res < 0) { + if (!strict) { + const char *args[] = { "OPEN", "SHARED", "LEAP" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "auth_alg", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "OPEN")) + goto error; + } + + if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "SHARED")) + goto error; + } + + if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "LEAP")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto error; + + return reply; + +error: + if (reply) + dbus_message_unref(reply); + return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning " + "interface capabilities."); +} + + +/** + * wpas_dbus_iface_add_network - Add a new configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing the object path of the new network + * + * Handler function for "addNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_ssid *ssid; + char *path = NULL; + + path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (path == NULL) { + perror("wpas_dbus_iface_scan_results[dbus]: out of " + "memory."); + wpa_printf(MSG_ERROR, "dbus control interface: not " + "enough memory to send scan results " + "signal."); + goto out; + } + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + reply = dbus_message_new_error(message, + WPAS_ERROR_ADD_NETWORK_ERROR, + "wpa_supplicant could not add " + "a network on this interface."); + goto out; + } + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + /* Construct the object path for this network. */ + snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NETWORKS_PART "/%d", + wpa_supplicant_get_dbus_path(wpa_s), + ssid->id); + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID); + +out: + free(path); + return reply; +} + + +/** + * wpas_dbus_iface_remove_network - Remove a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "removeNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface = NULL, *net_id = NULL; + int id; + struct wpa_ssid *ssid; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + /* Extract the network ID */ + iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); + if (iface == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + /* Ensure the network is actually a child of this interface */ + if (strcmp(iface, wpa_supplicant_get_dbus_path(wpa_s)) != 0) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + id = strtoul(net_id, NULL, 10); + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + reply = dbus_message_new_error(message, + WPAS_ERROR_REMOVE_NETWORK_ERROR, + "error removing the specified " + "on this interface."); + goto out; + } + + if (ssid == wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + reply = wpas_dbus_new_success_reply(message); + +out: + free(iface); + free(net_id); + return reply; +} + + +static const char *dont_quote[] = { + "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", + "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", + "bssid", NULL +}; + +static dbus_bool_t should_quote_opt(const char *key) +{ + int i = 0; + while (dont_quote[i] != NULL) { + if (strcmp(key, dont_quote[i]) == 0) + return FALSE; + i++; + } + return TRUE; +} + +/** + * wpas_dbus_iface_set_network - Set options for a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "set" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + DBusMessage *reply = NULL; + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessageIter iter, iter_dict; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + char *value = NULL; + size_t size = 50; + int ret; + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + goto out; + } + + /* Type conversions, since wpa_supplicant wants strings */ + if (entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + if (entry.array_len <= 0) + goto error; + + size = entry.array_len * 2 + 1; + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = wpa_snprintf_hex(value, size, + (u8 *) entry.bytearray_value, + entry.array_len); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_STRING) { + if (should_quote_opt(entry.key)) { + size = strlen(entry.str_value); + /* Zero-length option check */ + if (size <= 0) + goto error; + size += 3; /* For quotes and terminator */ + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = snprintf(value, size, "\"%s\"", + entry.str_value); + if (ret < 0 || (size_t) ret != (size - 1)) + goto error; + } else { + value = strdup(entry.str_value); + if (value == NULL) + goto error; + } + } else if (entry.type == DBUS_TYPE_UINT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = snprintf(value, size, "%u", entry.uint32_value); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_INT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = snprintf(value, size, "%d", entry.int32_value); + if (ret <= 0) + goto error; + } else + goto error; + + if (wpa_config_set(ssid, entry.key, value, 0) < 0) + goto error; + + if ((strcmp(entry.key, "psk") == 0 && + value[0] == '"' && ssid->ssid_len) || + (strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + + free(value); + wpa_dbus_dict_entry_clear(&entry); + continue; + + error: + free(value); + reply = wpas_dbus_new_invalid_opts_error(message, entry.key); + wpa_dbus_dict_entry_clear(&entry); + break; + } + + if (!reply) + reply = wpas_dbus_new_success_reply(message); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_enable_network - Mark a configured network as enabled + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "enable" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + 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 wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_disable_network - Mark a configured network as disabled + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "disable" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (ssid == wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + ssid->disabled = 1; + + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_select_network - Attempt association with a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "selectNetwork" method call of network interface. + */ +DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + struct wpa_ssid *ssid; + char *iface_obj_path = NULL; + char *network = NULL; + + if (strlen(dbus_message_get_signature(message)) == 0) { + /* Any network */ + 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); + } else { + const char *obj_path; + int nid; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + goto out; + } + + /* Extract the network number */ + iface_obj_path = wpas_dbus_decompose_object_path(op, + &network, + NULL); + if (iface_obj_path == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + /* Ensure the object path really points to this interface */ + obj_path = wpa_supplicant_get_dbus_path(wpa_s); + if (strcmp(iface_obj_path, obj_path) != 0) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + nid = strtoul(network, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, nid); + if (ssid == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + /* Finally, associate with the network */ + if (ssid != wpa_s->current_ssid && wpa_s->current_ssid) + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + /* Mark all other networks disabled and trigger reassociation + */ + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = (nid != ssid->id); + ssid = ssid->next; + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + reply = wpas_dbus_new_success_reply(message); + +out: + free(iface_obj_path); + free(network); + return reply; +} + + +/** + * wpas_dbus_iface_disconnect - Terminate the current connection + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "disconnect" method call of network interface. + */ +DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpa_s->disconnected = 1; + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_set_ap_scan - Control roaming mode + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "setAPScan" method call. + */ +DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_uint32_t ap_scan = 1; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + if (ap_scan > 2) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + wpa_s->conf->ap_scan = ap_scan; + reply = wpas_dbus_new_success_reply(message); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "setSmartcardModules" method call. + */ +DBusMessage * wpas_dbus_iface_set_smartcard_modules( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + char *opensc_engine_path = NULL; + char *pkcs11_engine_path = NULL; + char *pkcs11_module_path = NULL; + struct wpa_dbus_dict_entry entry; + + if (!dbus_message_iter_init(message, &iter)) + goto error; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!strcmp(entry.key, "opensc_engine_path") && + (entry.type == DBUS_TYPE_STRING)) { + opensc_engine_path = os_strdup(entry.str_value); + if (opensc_engine_path == NULL) + goto error; + } else if (!strcmp(entry.key, "pkcs11_engine_path") && + (entry.type == DBUS_TYPE_STRING)) { + pkcs11_engine_path = os_strdup(entry.str_value); + if (pkcs11_engine_path == NULL) + goto error; + } else if (!strcmp(entry.key, "pkcs11_module_path") && + (entry.type == DBUS_TYPE_STRING)) { + pkcs11_module_path = os_strdup(entry.str_value); + if (pkcs11_module_path == NULL) + goto error; + } else { + wpa_dbus_dict_entry_clear(&entry); + goto error; + } + wpa_dbus_dict_entry_clear(&entry); + } + +#ifdef EAP_TLS_OPENSSL + os_free(wpa_s->conf->opensc_engine_path); + wpa_s->conf->opensc_engine_path = opensc_engine_path; + os_free(wpa_s->conf->pkcs11_engine_path); + wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path; + os_free(wpa_s->conf->pkcs11_module_path); + wpa_s->conf->pkcs11_module_path = pkcs11_module_path; +#endif /* EAP_TLS_OPENSSL */ + + eapol_sm_deinit(wpa_s->eapol); + wpa_supplicant_init_eapol(wpa_s); + + return wpas_dbus_new_success_reply(message); + +error: + os_free(opensc_engine_path); + os_free(pkcs11_engine_path); + os_free(pkcs11_module_path); + return wpas_dbus_new_invalid_opts_error(message, NULL); +} + +/** + * wpas_dbus_iface_get_state - Get interface state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a STRING representing the current + * interface state + * + * Handler function for "state" method call. + */ +DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *str_state; + + reply = dbus_message_new_method_return(message); + if (reply != NULL) { + str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, + DBUS_TYPE_INVALID); + } + + return reply; +} + + +/** + * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Asks wpa_supplicant to internally store a one or more binary blobs. + */ +DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessageIter iter, iter_dict; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + struct wpa_config_blob *blob; + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + break; + } + + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) { + reply = wpas_dbus_new_invalid_opts_error( + message, "Byte array expected."); + break; + } + + if ((entry.array_len <= 0) || (entry.array_len > 65536) || + !strlen(entry.key)) { + reply = wpas_dbus_new_invalid_opts_error( + message, "Invalid array size."); + break; + } + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Not enough memory to add blob."); + break; + } + blob->data = os_zalloc(entry.array_len); + if (blob->data == NULL) { + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Not enough memory to add blob data."); + os_free(blob); + break; + } + + blob->name = os_strdup(entry.key); + blob->len = entry.array_len; + os_memcpy(blob->data, (u8 *) entry.bytearray_value, + entry.array_len); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Error adding blob."); + break; + } + + /* Success */ + wpa_config_remove_blob(wpa_s->conf, blob->name); + wpa_config_set_blob(wpa_s->conf, blob); + wpa_dbus_dict_entry_clear(&entry); + } + wpa_dbus_dict_entry_clear(&entry); + + return reply ? reply : wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_remove_blob - Remove named binary blobs + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Asks wpa_supplicant to remove one or more previously stored binary blobs. + */ +DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, array; + char *err_msg = NULL; + + dbus_message_iter_init(message, &iter); + + if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || + (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + dbus_message_iter_recurse(&iter, &array); + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { + const char *name; + + dbus_message_iter_get_basic(&array, &name); + if (!strlen(name)) + err_msg = "Invalid blob name."; + + if (wpa_config_remove_blob(wpa_s->conf, name) != 0) + err_msg = "Error removing blob."; + dbus_message_iter_next(&array); + } + + if (err_msg) { + return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, + err_msg); + } + + return wpas_dbus_new_success_reply(message); +} diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.h b/wpa_supplicant/ctrl_iface_dbus_handlers.h new file mode 100644 index 0000000000000..9660f9598a3cd --- /dev/null +++ b/wpa_supplicant/ctrl_iface_dbus_handlers.h @@ -0,0 +1,86 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CTRL_IFACE_DBUS_HANDLERS_H +#define CTRL_IFACE_DBUS_HANDLERS_H + +#ifdef CONFIG_CTRL_IFACE_DBUS + +DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message); + +DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res); + +DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_smartcard_modules( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +#endif /* CONFIG_CTRL_IFACE_DBUS */ + +#endif /* CTRL_IFACE_DBUS_HANDLERS_H */ + diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c new file mode 100644 index 0000000000000..e8b53b1945815 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_named_pipe.c @@ -0,0 +1,835 @@ +/* + * WPA Supplicant / Windows Named Pipe -based control interface + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "wpa_ctrl.h" + +#ifdef __MINGW32_VERSION +/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here + */ +#define SDDL_REVISION_1 1 +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA( + LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( + LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +#ifdef UNICODE +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorW +#else +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorA +#endif +#else /* __MINGW32_VERSION */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include <sddl.h> +#endif /* __MINGW32_VERSION */ + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +/* Per-interface ctrl_iface */ + +#define REQUEST_BUFSIZE 256 +#define REPLY_BUFSIZE 4096 + +struct ctrl_iface_priv; + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface clients + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_named_pipe.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */ + OVERLAPPED overlap; + struct wpa_ctrl_dst *next, *prev; + struct ctrl_iface_priv *priv; + HANDLE pipe; + int attached; + int debug_level; + int errors; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + struct wpa_ctrl_dst *ctrl_dst; + SECURITY_ATTRIBUTES attr; + int sec_attr_set; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst); +static void wpa_supplicant_ctrl_iface_receive(void *, void *); +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + +struct wpa_global_dst; +static void global_close_pipe(struct wpa_global_dst *dst); +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx); +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + + +static int ctrl_broken_pipe(HANDLE pipe, int used) +{ + DWORD err; + + if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL)) + return 0; + + err = GetLastError(); + if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used)) + return 1; + return 0; +} + + +static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + ctrl_close_pipe(dst); + } + dst = next; + } +} + + +static int ctrl_open_pipe(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst; + DWORD err; + TCHAR name[256]; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->debug_level = MSG_INFO; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_ctrl_iface_receive, dst, NULL); + +#ifdef UNICODE + _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + priv->wpa_s->ifname); +#else /* UNICODE */ + os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + priv->wpa_s->ifname); +#endif /* UNICODE */ + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 15, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, + priv->sec_attr_set ? &priv->attr : NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + ctrl_close_pipe(dst); + return -1; +} + + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + ctrl_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, ctrl_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len) +{ + struct wpa_supplicant *wpa_s = dst->priv->wpa_s; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + int new_attached = 0; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + dst->attached = 1; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached"); + new_attached = 1; + reply_len = 2; + } else if (os_strcmp(buf, "DETACH") == 0) { + dst->attached = 0; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached"); + reply_len = 2; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6); + dst->debug_level = atoi(buf + 6); + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + } + + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len == 2) { + send_buf = "OK\n"; + send_len = 3; + } else { + send_buf = "FAIL\n"; + send_len = 5; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + ctrl_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + ctrl_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_ctrl_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_ctrl_dst *dst = eloop_data; + struct ctrl_iface_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + ctrl_open_pipe(priv); + + /* Use write completion function to start reading a command */ + ctrl_iface_write_completed(0, 0, &dst->overlap); + + ctrl_flush_broken_pipes(priv); +} + + +static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) +{ + const char *sddl = NULL; + TCHAR *t_sddl; + + if (os_strncmp(params, "SDDL=", 5) == 0) + sddl = params + 5; + if (!sddl) { + sddl = os_strstr(params, " SDDL="); + if (sddl) + sddl += 6; + } + + if (!sddl) + return 0; + + wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl); + os_memset(&priv->attr, 0, sizeof(priv->attr)); + priv->attr.nLength = sizeof(priv->attr); + priv->attr.bInheritHandle = FALSE; + t_sddl = wpa_strdup_tchar(sddl); + if (t_sddl == NULL) + return -1; + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + t_sddl, SDDL_REVISION_1, + (PSECURITY_DESCRIPTOR *) (void *) + &priv->attr.lpSecurityDescriptor, + NULL)) { + os_free(t_sddl); + wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to " + "security descriptor: %d", + sddl, (int) GetLastError()); + return -1; + } + os_free(t_sddl); + + priv->sec_attr_set = 1; + + return 0; +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->wpa_s = wpa_s; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) { + os_free(priv); + return NULL; + } + + if (ctrl_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + while (priv->ctrl_dst) + ctrl_close_pipe(priv->ctrl_dst); + if (priv->sec_attr_set) + LocalFree(priv->attr.lpSecurityDescriptor); + os_free(priv); +} + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; + char *sbuf; + int llen; + DWORD written; + + dst = priv->ctrl_dst; + if (dst == NULL) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = os_strlen(levelstr); + sbuf = os_malloc(llen + len); + if (sbuf == NULL) + return; + + os_memcpy(sbuf, levelstr, llen); + os_memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (dst->attached && level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p", + dst); + if (!WriteFile(dst->pipe, sbuf, llen + len, &written, + NULL)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst " + "%p failed: %d", + dst, (int) GetLastError()); + dst->errors++; + if (dst->errors > 10) + ctrl_close_pipe(dst); + } else + dst->errors = 0; + } + idx++; + dst = next; + } + os_free(sbuf); +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + priv->wpa_s->ifname); + if (priv->ctrl_dst == NULL) + return; + WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE); +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv; + +struct wpa_global_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_global_dst + */ + OVERLAPPED overlap; + struct wpa_global_dst *next, *prev; + struct ctrl_iface_global_priv *priv; + HANDLE pipe; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + +struct ctrl_iface_global_priv { + struct wpa_global *global; + struct wpa_global_dst *ctrl_dst; +}; + + +static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + global_close_pipe(dst); + } + dst = next; + } +} + + +static int global_open_pipe(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst; + DWORD err; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_global_iface_receive, dst, NULL); + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 10, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + global_close_pipe(dst); + return -1; +} + + +static void global_close_pipe(struct wpa_global_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + global_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, global_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + /* FIX: if this was the pipe waiting for new global + * connections, at this point there are no open global pipes.. + * Should try to open a new pipe.. */ + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst, + size_t len) +{ + struct wpa_global *global = dst->priv->global; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + reply = wpa_supplicant_global_ctrl_iface_process(global, buf, + &reply_len); + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len) { + send_buf = "FAIL\n"; + send_len = 5; + } else { + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + global_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + global_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } +} + + +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_global_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx) +{ + struct wpa_global_dst *dst = eloop_data; + struct ctrl_iface_global_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + if (global_open_pipe(priv) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed"); + return; + } + + /* Use write completion function to start reading a command */ + global_iface_write_completed(0, 0, &dst->overlap); + + global_flush_broken_pipes(priv); +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->global = global; + + if (global_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + return priv; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + while (priv->ctrl_dst) + global_close_pipe(priv->ctrl_dst); + os_free(priv); +} diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c new file mode 100644 index 0000000000000..18e40405a7376 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -0,0 +1,561 @@ +/* + * WPA Supplicant / UDP socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "wpa_ctrl.h" + + +#define COOKIE_LEN 8 + +/* Per-interface ctrl_iface */ + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_udp.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + struct sockaddr_in addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + int sock; + struct wpa_ctrl_dst *ctrl_dst; + u8 cookie[COOKIE_LEN]; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + + +static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = priv->ctrl_dst; + priv->ctrl_dst = dst; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = priv->ctrl_dst; + while (dst) { + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + if (prev == NULL) + priv->ctrl_dst = dst->next; + else + prev->next = dst->next; + os_free(dst); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + return 0; + } + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = priv->ctrl_dst; + while (dst) { + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level %s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + dst->debug_level = atoi(level); + return 0; + } + dst = dst->next; + } + + return -1; +} + + +static char * +wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv, + size_t *reply_len) +{ + char *reply; + reply = os_malloc(7 + 2 * COOKIE_LEN + 1); + if (reply == NULL) { + *reply_len = 1; + return NULL; + } + + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + priv->cookie, COOKIE_LEN); + + *reply_len = 7 + 2 * COOKIE_LEN; + return reply; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv = sock_ctx; + char buf[256], *pos; + int res; + struct sockaddr_in from; + socklen_t fromlen = sizeof(from); + char *reply = NULL; + size_t reply_len = 0; + int new_attached = 0; + u8 cookie[COOKIE_LEN]; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { + /* + * The OS networking stack is expected to drop this kind of + * frames since the socket is bound to only localhost address. + * Just in case, drop the frame if it is coming from any other + * address. + */ + wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " + "source %s", inet_ntoa(from.sin_addr)); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "GET_COOKIE") == 0) { + reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len); + goto done; + } + + /* + * Require that the client includes a prefix with the 'cookie' value + * fetched with GET_COOKIE command. This is used to verify that the + * client has access to a bidirectional link over UDP in order to + * avoid attacks using forged localhost IP address even if the OS does + * not block such frames from remote destinations. + */ + if (os_strncmp(buf, "COOKIE=", 7) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " + "drop request"); + return; + } + + if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " + "request - drop request"); + return; + } + + if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " + "drop request"); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; + + if (os_strcmp(pos, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + reply_len = 1; + else { + new_attached = 1; + reply_len = 2; + } + } else if (os_strcmp(pos, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strncmp(pos, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, + pos + 6)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos, + &reply_len); + } + + done: + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len == 1) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } else if (reply_len == 2) { + sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, + fromlen); + } + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct sockaddr_in addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->wpa_s = wpa_s; + priv->sock = -1; + os_get_random(priv->cookie, COOKIE_LEN); + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(WPA_CTRL_IFACE_PORT); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_INET)"); + goto fail; + } + + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, + wpa_s, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (priv->sock > -1) { + eloop_unregister_read_sock(priv->sock); + if (priv->ctrl_dst) { + /* + * Wait a second before closing the control socket if + * there are any attached monitors in order to allow + * them to receive any pending messages. + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " + "monitors to receive messages"); + os_sleep(1, 0); + } + close(priv->sock); + priv->sock = -1; + } + + dst = priv->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); + } + os_free(priv); +} + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; + char *sbuf; + int llen; + + dst = priv->ctrl_dst; + if (priv->sock < 0 || dst == NULL) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = os_strlen(levelstr); + sbuf = os_malloc(llen + len); + if (sbuf == NULL) + return; + + os_memcpy(sbuf, levelstr, llen); + os_memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", + inet_ntoa(dst->addr.sin_addr), + ntohs(dst->addr.sin_port)); + if (sendto(priv->sock, sbuf, llen + len, 0, + (struct sockaddr *) &dst->addr, + sizeof(dst->addr)) < 0) { + perror("sendto(CTRL_IFACE monitor)"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + priv, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } + os_free(sbuf); +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + priv->wpa_s->ifname); + eloop_wait_for_read_sock(priv->sock); +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv { + int sock; + u8 cookie[COOKIE_LEN]; +}; + + +static char * +wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, + size_t *reply_len) +{ + char *reply; + reply = os_malloc(7 + 2 * COOKIE_LEN + 1); + if (reply == NULL) { + *reply_len = 1; + return NULL; + } + + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + priv->cookie, COOKIE_LEN); + + *reply_len = 7 + 2 * COOKIE_LEN; + return reply; +} + + +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct ctrl_iface_global_priv *priv = sock_ctx; + char buf[256], *pos; + int res; + struct sockaddr_in from; + socklen_t fromlen = sizeof(from); + char *reply; + size_t reply_len; + u8 cookie[COOKIE_LEN]; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { + /* + * The OS networking stack is expected to drop this kind of + * frames since the socket is bound to only localhost address. + * Just in case, drop the frame if it is coming from any other + * address. + */ + wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " + "source %s", inet_ntoa(from.sin_addr)); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "GET_COOKIE") == 0) { + reply = wpa_supplicant_global_get_cookie(priv, &reply_len); + goto done; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " + "drop request"); + return; + } + + if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " + "request - drop request"); + return; + } + + if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " + "drop request"); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; + + reply = wpa_supplicant_global_ctrl_iface_process(global, pos, + &reply_len); + + done: + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + struct sockaddr_in addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->sock = -1; + os_get_random(priv->cookie, COOKIE_LEN); + + if (global->params.ctrl_interface == NULL) + return priv; + + wpa_printf(MSG_DEBUG, "Global control interface '%s'", + global->params.ctrl_interface); + + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_INET)"); + goto fail; + } + + eloop_register_read_sock(priv->sock, + wpa_supplicant_global_ctrl_iface_receive, + global, priv); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + if (priv->sock >= 0) { + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + } + os_free(priv); +} diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c new file mode 100644 index 0000000000000..bf6328e75e5a4 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -0,0 +1,699 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> +#include <sys/stat.h> +#include <grp.h> + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" + +/* Per-interface ctrl_iface */ + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_unix.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + struct sockaddr_un addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + int sock; + struct wpa_ctrl_dst *ctrl_dst; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + + +static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = priv->ctrl_dst; + priv->ctrl_dst = dst; + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", + (u8 *) from->sun_path, fromlen - sizeof(from->sun_family)); + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = priv->ctrl_dst; + while (dst) { + if (fromlen == dst->addrlen && + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - sizeof(from->sun_family)) == 0) { + if (prev == NULL) + priv->ctrl_dst = dst->next; + else + prev->next = dst->next; + os_free(dst); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, + fromlen - sizeof(from->sun_family)); + return 0; + } + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = priv->ctrl_dst; + while (dst) { + if (fromlen == dst->addrlen && + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - sizeof(from->sun_family)) == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level", (u8 *) from->sun_path, + fromlen - sizeof(from->sun_family)); + dst->debug_level = atoi(level); + return 0; + } + dst = dst->next; + } + + return -1; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv = sock_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply = NULL; + size_t reply_len = 0; + int new_attached = 0; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + reply_len = 1; + else { + new_attached = 1; + reply_len = 2; + } + } else if (os_strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, + buf + 6)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + } + + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len == 1) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } else if (reply_len == 2) { + sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, + fromlen); + } + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) +{ + char *buf; + size_t len; + char *pbuf, *dir = NULL, *gid_str = NULL; + int res; + + if (wpa_s->conf->ctrl_interface == NULL) + return NULL; + + pbuf = os_strdup(wpa_s->conf->ctrl_interface); + if (pbuf == NULL) + return NULL; + if (os_strncmp(pbuf, "DIR=", 4) == 0) { + dir = pbuf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else + dir = pbuf; + + len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2; + buf = os_malloc(len); + if (buf == NULL) { + os_free(pbuf); + return NULL; + } + + res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname); + if (res < 0 || (size_t) res >= len) { + os_free(pbuf); + os_free(buf); + return NULL; + } +#ifdef __CYGWIN__ + { + /* Windows/WinPcap uses interface names that are not suitable + * as a file name - convert invalid chars to underscores */ + char *pos = buf; + while (*pos) { + if (*pos == '\\') + *pos = '_'; + pos++; + } + } +#endif /* __CYGWIN__ */ + os_free(pbuf); + return buf; +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct sockaddr_un addr; + char *fname = NULL; + gid_t gid = 0; + int gid_set = 0; + char *buf, *dir = NULL, *gid_str = NULL; + struct group *grp; + char *endp; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->wpa_s = wpa_s; + priv->sock = -1; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + buf = os_strdup(wpa_s->conf->ctrl_interface); + if (buf == NULL) + goto fail; + if (os_strncmp(buf, "DIR=", 4) == 0) { + dir = buf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else { + dir = buf; + gid_str = wpa_s->conf->ctrl_interface_group; + } + + if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (gid_str) { + grp = getgrnam(gid_str); + if (grp) { + gid = grp->gr_gid; + gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) gid, gid_str); + } else { + /* Group name not found - try to parse this as gid */ + gid = strtol(gid_str, &endp, 10); + if (*gid_str == '\0' || *endp != '\0') { + wpa_printf(MSG_ERROR, "CTRL: Invalid group " + "'%s'", gid_str); + goto fail; + } + gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) gid); + } + } + + if (gid_set && chown(dir, -1, gid) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >= + sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded"); + goto fail; + } + + priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (gid_set && chown(fname, -1, gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + os_free(fname); + + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, + wpa_s, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + os_free(buf); + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + if (fname) { + unlink(fname); + os_free(fname); + } + os_free(buf); + return NULL; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (priv->sock > -1) { + char *fname; + char *buf, *dir = NULL, *gid_str = NULL; + eloop_unregister_read_sock(priv->sock); + if (priv->ctrl_dst) { + /* + * Wait a second before closing the control socket if + * there are any attached monitors in order to allow + * them to receive any pending messages. + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " + "monitors to receive messages"); + os_sleep(1, 0); + } + close(priv->sock); + priv->sock = -1; + fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s); + if (fname) { + unlink(fname); + os_free(fname); + } + + buf = os_strdup(priv->wpa_s->conf->ctrl_interface); + if (buf == NULL) + goto free_dst; + if (os_strncmp(buf, "DIR=", 4) == 0) { + dir = buf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else + dir = buf; + + if (rmdir(dir) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + os_free(buf); + } + +free_dst: + dst = priv->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); + } + os_free(priv); +} + + +/** + * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * @level: Priority level of the message + * @buf: Message data + * @len: Message length + * + * Send a packet to all monitor programs attached to the control interface. + */ +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx, res; + struct msghdr msg; + struct iovec io[2]; + + dst = priv->ctrl_dst; + if (priv->sock < 0 || dst == NULL) + return; + + res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + if (res < 0 || (size_t) res >= sizeof(levelstr)) + return; + io[0].iov_base = levelstr; + io[0].iov_len = os_strlen(levelstr); + io[1].iov_base = (char *) buf; + io[1].iov_len = len; + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", + (u8 *) dst->addr.sun_path, dst->addrlen - + sizeof(dst->addr.sun_family)); + msg.msg_name = (void *) &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(priv->sock, &msg, 0) < 0) { + perror("sendmsg(CTRL_IFACE monitor)"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + priv, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + for (;;) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " + "attach", priv->wpa_s->ifname); + eloop_wait_for_read_sock(priv->sock); + + res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + continue; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + /* handle ATTACH signal of first monitor interface */ + if (!wpa_supplicant_ctrl_iface_attach(priv, &from, + fromlen)) { + sendto(priv->sock, "OK\n", 3, 0, + (struct sockaddr *) &from, fromlen); + /* OK to continue */ + return; + } else { + sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen); + } + } else { + /* return FAIL for all other signals */ + sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen); + } + } +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv { + struct wpa_global *global; + int sock; +}; + + +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_global *global = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply; + size_t reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + + reply = wpa_supplicant_global_ctrl_iface_process(global, buf, + &reply_len); + + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + struct sockaddr_un addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->global = global; + priv->sock = -1; + + if (global->params.ctrl_interface == NULL) + return priv; + + wpa_printf(MSG_DEBUG, "Global control interface '%s'", + global->params.ctrl_interface); + + priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, global->params.ctrl_interface, + sizeof(addr.sun_path)); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + if (connect(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(global->params.ctrl_interface) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + global->params.ctrl_interface); + goto fail; + } + if (bind(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", + global->params.ctrl_interface); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", + global->params.ctrl_interface); + goto fail; + } + } + + eloop_register_read_sock(priv->sock, + wpa_supplicant_global_ctrl_iface_receive, + global, NULL); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + if (priv->sock >= 0) { + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + } + if (priv->global->params.ctrl_interface) + unlink(priv->global->params.ctrl_interface); + os_free(priv); +} diff --git a/wpa_supplicant/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus-wpa_supplicant.conf new file mode 100644 index 0000000000000..51a29e3a89c0f --- /dev/null +++ b/wpa_supplicant/dbus-wpa_supplicant.conf @@ -0,0 +1,16 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="fi.epitest.hostap.WPASupplicant"/> + + <allow send_destination="fi.epitest.hostap.WPASupplicant"/> + <allow send_interface="fi.epitest.hostap.WPASupplicant"/> + </policy> + <policy context="default"> + <deny own="fi.epitest.hostap.WPASupplicant"/> + <deny send_destination="fi.epitest.hostap.WPASupplicant"/> + <deny send_interface="fi.epitest.hostap.WPASupplicant"/> + </policy> +</busconfig> diff --git a/wpa_supplicant/dbus-wpa_supplicant.service b/wpa_supplicant/dbus-wpa_supplicant.service new file mode 100644 index 0000000000000..a9ce1ecda63fb --- /dev/null +++ b/wpa_supplicant/dbus-wpa_supplicant.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=fi.epitest.hostap.WPASupplicant +Exec=/sbin/wpa_supplicant -u +User=root diff --git a/wpa_supplicant/dbus_dict_helpers.c b/wpa_supplicant/dbus_dict_helpers.c new file mode 100644 index 0000000000000..f93fc9d700133 --- /dev/null +++ b/wpa_supplicant/dbus_dict_helpers.c @@ -0,0 +1,976 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <dbus/dbus.h> + +#include "common.h" +#include "dbus_dict_helpers.h" + + +/** + * Start a dict in a dbus message. Should be paired with a call to + * wpa_dbus_dict_close_write(). + * + * @param iter A valid dbus message iterator + * @param iter_dict (out) A dict iterator to pass to further dict functions + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + dbus_bool_t result; + + if (!iter || !iter_dict) + return FALSE; + + result = dbus_message_iter_open_container( + iter, + DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + iter_dict); + return result; +} + + +/** + * End a dict element in a dbus message. Should be paired with + * a call to wpa_dbus_dict_open_write(). + * + * @param iter valid dbus message iterator, same as passed to + * wpa_dbus_dict_open_write() + * @param iter_dict a dbus dict iterator returned from + * wpa_dbus_dict_open_write() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + if (!iter || !iter_dict) + return FALSE; + + return dbus_message_iter_close_container(iter, iter_dict); +} + + +static const char * _wpa_get_type_as_string_from_type(const int type) +{ + switch(type) { + case DBUS_TYPE_BYTE: + return DBUS_TYPE_BYTE_AS_STRING; + case DBUS_TYPE_BOOLEAN: + return DBUS_TYPE_BOOLEAN_AS_STRING; + case DBUS_TYPE_INT16: + return DBUS_TYPE_INT16_AS_STRING; + case DBUS_TYPE_UINT16: + return DBUS_TYPE_UINT16_AS_STRING; + case DBUS_TYPE_INT32: + return DBUS_TYPE_INT32_AS_STRING; + case DBUS_TYPE_UINT32: + return DBUS_TYPE_UINT32_AS_STRING; + case DBUS_TYPE_INT64: + return DBUS_TYPE_INT64_AS_STRING; + case DBUS_TYPE_UINT64: + return DBUS_TYPE_UINT64_AS_STRING; + case DBUS_TYPE_DOUBLE: + return DBUS_TYPE_DOUBLE_AS_STRING; + case DBUS_TYPE_STRING: + return DBUS_TYPE_STRING_AS_STRING; + case DBUS_TYPE_OBJECT_PATH: + return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case DBUS_TYPE_ARRAY: + return DBUS_TYPE_ARRAY_AS_STRING; + default: + return NULL; + } +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_start( + DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry, + const char *key, const int value_type) +{ + if (!dbus_message_iter_open_container(iter_dict, + DBUS_TYPE_DICT_ENTRY, NULL, + iter_dict_entry)) + return FALSE; + + if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, + &key)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_end( + DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val) +{ + if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val)) + return FALSE; + if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict, + const char *key, + const int value_type, + const void *value) +{ + DBusMessageIter iter_dict_entry, iter_dict_val; + const char *type_as_string = NULL; + + type_as_string = _wpa_get_type_as_string_from_type(value_type); + if (!type_as_string) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, + key, value_type)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_entry, + DBUS_TYPE_VARIANT, + type_as_string, &iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( + DBusMessageIter *iter_dict, const char *key, + const char *value, const dbus_uint32_t value_len) +{ + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + dbus_uint32_t i; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, + key, DBUS_TYPE_ARRAY)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &iter_array)) + return FALSE; + + for (i = 0; i < value_len; i++) { + if (!dbus_message_iter_append_basic(&iter_array, + DBUS_TYPE_BYTE, + &(value[i]))) + return FALSE; + } + + if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val)) + return FALSE; + + return TRUE; +} + + +/** + * Add a string entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The string value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, + const char *key, const char *value) +{ + if (!key || !value) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_STRING, + &value); +} + + +/** + * Add a byte entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The byte value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, + const char *key, const char value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE, + &value); +} + + +/** + * Add a boolean entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The boolean value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, + const char *key, const dbus_bool_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, + DBUS_TYPE_BOOLEAN, &value); +} + + +/** + * Add a 16-bit signed integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 16-bit signed integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict, + const char *key, + const dbus_int16_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT16, + &value); +} + + +/** + * Add a 16-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 16-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint16_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT16, + &value); +} + + +/** + * Add a 32-bit signed integer to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 32-bit signed integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict, + const char *key, + const dbus_int32_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT32, + &value); +} + + +/** + * Add a 32-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 32-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint32_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT32, + &value); +} + + +/** + * Add a 64-bit integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 64-bit integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, + const char *key, + const dbus_int64_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64, + &value); +} + + +/** + * Add a 64-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 64-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint64_t value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64, + &value); +} + + +/** + * Add a double-precision floating point entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The double-precision floating point value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, + const char * key, + const double value) +{ + if (!key) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE, + &value); +} + + +/** + * Add a DBus object path entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The DBus object path value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, + const char *key, + const char *value) +{ + if (!key || !value) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, + DBUS_TYPE_OBJECT_PATH, &value); +} + + +/** + * Add a byte array entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The byte array + * @param value_len The length of the byte array, in bytes + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, + const char *key, + const char *value, + const dbus_uint32_t value_len) +{ + if (!key) + return FALSE; + if (!value && (value_len != 0)) + return FALSE; + return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value, + value_len); +} + + +/** + * Begin a string array entry in the dict + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param iter_dict_entry A private DBusMessageIter provided by the caller to + * be passed to wpa_dbus_dict_end_string_array() + * @param iter_dict_val A private DBusMessageIter provided by the caller to + * be passed to wpa_dbus_dict_end_string_array() + * @param iter_array On return, the DBusMessageIter to be passed to + * wpa_dbus_dict_string_array_add_element() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, + key, DBUS_TYPE_ARRAY)) + return FALSE; + + if (!dbus_message_iter_open_container(iter_dict_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING, + iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + iter_array)) + return FALSE; + + return TRUE; +} + + +/** + * Add a single string element to a string array dict entry + * + * @param iter_array A valid DBusMessageIter returned from + * wpa_dbus_dict_begin_string_array()'s + * iter_array parameter + * @param elem The string element to be added to the dict entry's string array + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, + const char *elem) +{ + if (!iter_array || !elem) + return FALSE; + + return dbus_message_iter_append_basic(iter_array, DBUS_TYPE_STRING, + &elem); +} + + +/** + * End a string array dict entry + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param iter_dict_entry A private DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @param iter_dict_val A private DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @param iter_array A DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) + return FALSE; + + if (!dbus_message_iter_close_container(iter_dict_val, iter_array)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, + iter_dict_val)) + return FALSE; + + return TRUE; +} + + +/** + * Convenience function to add an entire string array to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param items The array of strings + * @param num_items The number of strings in the array + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, + const char *key, + const char **items, + const dbus_uint32_t num_items) +{ + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + dbus_uint32_t i; + + if (!key) + return FALSE; + if (!items && (num_items != 0)) + return FALSE; + + if (!wpa_dbus_dict_begin_string_array(iter_dict, key, + &iter_dict_entry, &iter_dict_val, + &iter_array)) + return FALSE; + + for (i = 0; i < num_items; i++) { + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + items[i])) + return FALSE; + } + + if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array)) + return FALSE; + + return TRUE; +} + + +/*****************************************************/ +/* Stuff for reading dicts */ +/*****************************************************/ + +/** + * Start reading from a dbus dict. + * + * @param iter A valid DBusMessageIter pointing to the start of the dict + * @param iter_dict (out) A DBusMessageIter to be passed to + * wpa_dbus_dict_read_next_entry() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + if (!iter || !iter_dict) + return FALSE; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) + return FALSE; + + dbus_message_iter_recurse(iter, iter_dict); + return TRUE; +} + + +#define BYTE_ARRAY_CHUNK_SIZE 34 +#define BYTE_ARRAY_ITEM_SIZE (sizeof(char)) + +static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( + DBusMessageIter *iter, int array_type, + struct wpa_dbus_dict_entry *entry) +{ + dbus_uint32_t count = 0; + dbus_bool_t success = FALSE; + char *buffer; + + entry->bytearray_value = NULL; + entry->array_type = DBUS_TYPE_BYTE; + + buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE); + if (!buffer) { + perror("_wpa_dbus_dict_entry_get_byte_array[dbus]: out of " + "memory"); + goto done; + } + + entry->bytearray_value = buffer; + entry->array_len = 0; + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) { + char byte; + + if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) { + buffer = realloc(buffer, BYTE_ARRAY_ITEM_SIZE * + (count + BYTE_ARRAY_CHUNK_SIZE)); + if (buffer == NULL) { + perror("_wpa_dbus_dict_entry_get_byte_array[" + "dbus] out of memory trying to " + "retrieve the string array"); + goto done; + } + } + entry->bytearray_value = buffer; + + dbus_message_iter_get_basic(iter, &byte); + entry->bytearray_value[count] = byte; + entry->array_len = ++count; + dbus_message_iter_next(iter); + } + + /* Zero-length arrays are valid. */ + if (entry->array_len == 0) { + free(entry->bytearray_value); + entry->bytearray_value = NULL; + } + + success = TRUE; + +done: + return success; +} + + +#define STR_ARRAY_CHUNK_SIZE 8 +#define STR_ARRAY_ITEM_SIZE (sizeof(char *)) + +static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( + DBusMessageIter *iter, int array_type, + struct wpa_dbus_dict_entry *entry) +{ + dbus_uint32_t count = 0; + dbus_bool_t success = FALSE; + char **buffer; + + entry->strarray_value = NULL; + entry->array_type = DBUS_TYPE_STRING; + + buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE); + if (buffer == NULL) { + perror("_wpa_dbus_dict_entry_get_string_array[dbus] out of " + "memory trying to retrieve a string array"); + goto done; + } + + entry->strarray_value = buffer; + entry->array_len = 0; + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { + const char *value; + char *str; + + if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) { + buffer = realloc(buffer, STR_ARRAY_ITEM_SIZE * + (count + STR_ARRAY_CHUNK_SIZE)); + if (buffer == NULL) { + perror("_wpa_dbus_dict_entry_get_string_array[" + "dbus] out of memory trying to " + "retrieve the string array"); + goto done; + } + } + entry->strarray_value = buffer; + + dbus_message_iter_get_basic(iter, &value); + str = strdup(value); + if (str == NULL) { + perror("_wpa_dbus_dict_entry_get_string_array[dbus] " + "out of memory trying to duplicate the string " + "array"); + goto done; + } + entry->strarray_value[count] = str; + entry->array_len = ++count; + dbus_message_iter_next(iter); + } + + /* Zero-length arrays are valid. */ + if (entry->array_len == 0) { + free(entry->strarray_value); + entry->strarray_value = NULL; + } + + success = TRUE; + +done: + return success; +} + + +static dbus_bool_t _wpa_dbus_dict_entry_get_array( + DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry) +{ + int array_type = dbus_message_iter_get_element_type(iter_dict_val); + dbus_bool_t success = FALSE; + DBusMessageIter iter_array; + + if (!entry) + return FALSE; + + dbus_message_iter_recurse(iter_dict_val, &iter_array); + + switch (array_type) { + case DBUS_TYPE_BYTE: + success = _wpa_dbus_dict_entry_get_byte_array(&iter_array, + array_type, + entry); + break; + case DBUS_TYPE_STRING: + success = _wpa_dbus_dict_entry_get_string_array(&iter_array, + array_type, + entry); + break; + default: + break; + } + + return success; +} + + +static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant( + struct wpa_dbus_dict_entry *entry, DBusMessageIter *iter_dict_val) +{ + dbus_bool_t success = TRUE; + + switch (entry->type) { + case DBUS_TYPE_STRING: { + const char *v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->str_value = strdup(v); + break; + } + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->bool_value = v; + break; + } + case DBUS_TYPE_BYTE: { + char v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->byte_value = v; + break; + } + case DBUS_TYPE_INT16: { + dbus_int16_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->int16_value = v; + break; + } + case DBUS_TYPE_UINT16: { + dbus_uint16_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->uint16_value = v; + break; + } + case DBUS_TYPE_INT32: { + dbus_int32_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->int32_value = v; + break; + } + case DBUS_TYPE_UINT32: { + dbus_uint32_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->uint32_value = v; + break; + } + case DBUS_TYPE_INT64: { + dbus_int64_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->int64_value = v; + break; + } + case DBUS_TYPE_UINT64: { + dbus_uint64_t v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->uint64_value = v; + break; + } + case DBUS_TYPE_DOUBLE: { + double v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->double_value = v; + break; + } + case DBUS_TYPE_OBJECT_PATH: { + char *v; + dbus_message_iter_get_basic(iter_dict_val, &v); + entry->str_value = strdup(v); + break; + } + case DBUS_TYPE_ARRAY: { + success = _wpa_dbus_dict_entry_get_array(iter_dict_val, entry); + break; + } + default: + success = FALSE; + break; + } + + return success; +} + + +/** + * Read the current key/value entry from the dict. Entries are dynamically + * allocated when needed and must be freed after use with the + * wpa_dbus_dict_entry_clear() function. + * + * The returned entry object will be filled with the type and value of the next + * entry in the dict, or the type will be DBUS_TYPE_INVALID if an error + * occurred. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_read() + * @param entry A valid dict entry object into which the dict key and value + * will be placed + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, + struct wpa_dbus_dict_entry * entry) +{ + DBusMessageIter iter_dict_entry, iter_dict_val; + int type; + const char *key; + + if (!iter_dict || !entry) + goto error; + + if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) + goto error; + + dbus_message_iter_recurse(iter_dict, &iter_dict_entry); + dbus_message_iter_get_basic(&iter_dict_entry, &key); + entry->key = key; + + if (!dbus_message_iter_next(&iter_dict_entry)) + goto error; + type = dbus_message_iter_get_arg_type(&iter_dict_entry); + if (type != DBUS_TYPE_VARIANT) + goto error; + + dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val); + entry->type = dbus_message_iter_get_arg_type(&iter_dict_val); + if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) + goto error; + + dbus_message_iter_next(iter_dict); + return TRUE; + +error: + if (entry) { + wpa_dbus_dict_entry_clear(entry); + entry->type = DBUS_TYPE_INVALID; + entry->array_type = DBUS_TYPE_INVALID; + } + + return FALSE; +} + + +/** + * Return whether or not there are additional dictionary entries. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_read() + * @return TRUE if more dict entries exists, FALSE if no more dict entries + * exist + */ +dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict) +{ + if (!iter_dict) { + perror("wpa_dbus_dict_has_dict_entry[dbus]: out of memory"); + return FALSE; + } + return dbus_message_iter_get_arg_type(iter_dict) == + DBUS_TYPE_DICT_ENTRY; +} + + +/** + * Free any memory used by the entry object. + * + * @param entry The entry object + */ +void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry) +{ + unsigned int i; + + if (!entry) + return; + switch (entry->type) { + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_STRING: + free(entry->str_value); + break; + case DBUS_TYPE_ARRAY: + switch (entry->array_type) { + case DBUS_TYPE_BYTE: + free(entry->bytearray_value); + break; + case DBUS_TYPE_STRING: + for (i = 0; i < entry->array_len; i++) + free(entry->strarray_value[i]); + free(entry->strarray_value); + break; + } + break; + } + + memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); +} diff --git a/wpa_supplicant/dbus_dict_helpers.h b/wpa_supplicant/dbus_dict_helpers.h new file mode 100644 index 0000000000000..f873efcbcb137 --- /dev/null +++ b/wpa_supplicant/dbus_dict_helpers.h @@ -0,0 +1,135 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DBUS_DICT_HELPERS_H +#define DBUS_DICT_HELPERS_H + +/* + * Adding a dict to a DBusMessage + */ + +dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, + const char *key, const char *value); + +dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, + const char *key, const char value); + +dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, + const char *key, + const dbus_bool_t value); + +dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict, + const char *key, + const dbus_int16_t value); + +dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint16_t value); + +dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict, + const char *key, + const dbus_int32_t value); + +dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint32_t value); + +dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, + const char *key, + const dbus_int64_t value); + +dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint64_t value); + +dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, + const char *key, + const double value); + +dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, + const char *key, + const char *value); + +dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, + const char *key, + const char *value, + const dbus_uint32_t value_len); + +/* Manual construction and addition of string array elements */ +dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + +dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, + const char *elem); + +dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + +/* Convenience function to add a whole string list */ +dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, + const char *key, + const char **items, + const dbus_uint32_t num_items); + +/* + * Reading a dict from a DBusMessage + */ + +struct wpa_dbus_dict_entry { + int type; /** the dbus type of the dict entry's value */ + int array_type; /** the dbus type of the array elements if the dict + entry value contains an array */ + const char *key; /** key of the dict entry */ + + /** Possible values of the property */ + union { + char *str_value; + char byte_value; + dbus_bool_t bool_value; + dbus_int16_t int16_value; + dbus_uint16_t uint16_value; + dbus_int32_t int32_value; + dbus_uint32_t uint32_value; + dbus_int64_t int64_value; + dbus_uint64_t uint64_value; + double double_value; + char *bytearray_value; + char **strarray_value; + }; + dbus_uint32_t array_len; /** length of the array if the dict entry's + value contains an array */ +}; + +dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, + struct wpa_dbus_dict_entry *entry); + +dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict); + +void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry); + +#endif /* DBUS_DICT_HELPERS_H */ diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig new file mode 100644 index 0000000000000..dd9460d7b8e7c --- /dev/null +++ b/wpa_supplicant/defconfig @@ -0,0 +1,382 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# used to fix build issues on such systems (krb5.h not found). +#CFLAGS += -I/usr/include/kerberos + +# Example configuration for various cross-compilation platforms + +#### sveasoft (e.g., for Linksys WRT54G) ###################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS += -I../src/include -I../../src/router/openssl/include +#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl +############################################################################### + +#### openwrt (e.g., for Linksys WRT54G) ####################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \ +# -I../WRT54GS/release/src/include +#LIBS = -lssl +############################################################################### + + +# Driver interface for Host AP driver +CONFIG_DRIVER_HOSTAP=y + +# Driver interface for Agere driver +#CONFIG_DRIVER_HERMES=y +# Change include directories to match with the local setup +#CFLAGS += -I../../hcf -I../../include -I../../include/hcf +#CFLAGS += -I../../include/wireless + +# Driver interface for madwifi driver +#CONFIG_DRIVER_MADWIFI=y +# Set include directory to the madwifi source tree +#CFLAGS += -I../../madwifi + +# Driver interface for Prism54 driver +# (Note: Prism54 is not yet supported, i.e., this will not work as-is and is +# for developers only) +#CONFIG_DRIVER_PRISM54=y + +# Driver interface for ndiswrapper +#CONFIG_DRIVER_NDISWRAPPER=y + +# Driver interface for Atmel driver +CONFIG_DRIVER_ATMEL=y + +# Driver interface for old Broadcom driver +# Please note that the newer Broadcom driver ("hybrid Linux driver") supports +# Linux wireless extensions and does not need (or even work) with the old +# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver. +#CONFIG_DRIVER_BROADCOM=y +# Example path for wlioctl.h; change to match your configuration +#CFLAGS += -I/opt/WRT54GS/release/src/include + +# Driver interface for Intel ipw2100/2200 driver +#CONFIG_DRIVER_IPW=y + +# Driver interface for Ralink driver +#CONFIG_DRIVER_RALINK=y + +# Driver interface for generic Linux wireless extensions +CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Driver interface for wired Ethernet drivers +CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-FAST +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +#CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +#CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +#CONFIG_WPS=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# named_pipe = Windows Named Pipe (default for Windows) +# y = use default (backwards compatibility) +# If this option is commented out, control interface is not included in the +# build. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to +# save about 1 kB in code size when building only WPA-Personal (no EAP support) +# or 6 kB if building for WPA-Enterprise. +#CONFIG_NO_WPA2=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=y + +# Remove AES extra functions. This can be used to reduce code size by about +# 1.5 kB by removing extra AES modes that are not needed for commonly used +# client configurations (they are needed for some EAP types). +#CONFIG_NO_AES_EXTRAS=y + +# Disable scan result processing (ap_mode=1) to save code size by about 1 kB. +# This can be used if ap_scan=1 mode is never enabled. +#CONFIG_NO_SCAN_PROCESSING=y + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operatins system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +#CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +# eloop_none = Empty template +#CONFIG_ELOOP=eloop + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +#CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +#CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA) +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# Whether to enable TLS/IA support, which is required for EAP-TTLSv1. +# You need CONFIG_TLS=gnutls for this to have any effect. Please note that +# even though the core GnuTLS library is released under LGPL, this extra +# library uses GPL and as such, the terms of GPL apply to the combination +# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not +# apply for distribution of the resulting binary. +#CONFIG_GNUTLS_EXTRA=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#CONFIG_NDIS_EVENTS_INTEGRATED=y +#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" + +# Add support for DBus control interface +#CONFIG_CTRL_IFACE_DBUS=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# Include client MLME (management frame processing). +# This can be used to move MLME processing of Linux mac80211 stack into user +# space. Please note that this is currently only available with +# driver_nl80211.c and only with a modified version of Linux kernel and +# wpa_supplicant. +#CONFIG_CLIENT_MLME=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y diff --git a/wpa_supplicant/doc/.gitignore b/wpa_supplicant/doc/.gitignore new file mode 100644 index 0000000000000..59e4eb89ac2d7 --- /dev/null +++ b/wpa_supplicant/doc/.gitignore @@ -0,0 +1,4 @@ +html +latex +wpa_supplicant.eps +wpa_supplicant.png diff --git a/wpa_supplicant/doc/code_structure.doxygen b/wpa_supplicant/doc/code_structure.doxygen new file mode 100644 index 0000000000000..6398ff3443300 --- /dev/null +++ b/wpa_supplicant/doc/code_structure.doxygen @@ -0,0 +1,322 @@ +/** +\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 tls_func "TLS library" | +\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 + + +\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) + +sha256.c and sha256.h + SHA-256 (replaced with a crypto library if TLS support is included) + +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_openssl.c + Wrapper functions for libcrypto (OpenSSL) + +crypto_internal.c + Wrapper functions for internal crypto implementation + +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_internal.c + TLS library for internal TLS implementation + +tls_gnutls.c + TLS library wrapper for GnuTLS + + +\section tls_func TLS library + +asn1.c and asn1.h + ASN.1 DER parsing + +bignum.c and bignum.h + Big number math + +rsa.c and rsa.h + RSA + +x509v3.c and x509v3.h + X.509v3 certificate parsing and processing + +tlsv1_client.c, tlsv1_client.h + TLSv1 client (RFC 2246) + +tlsv1_client_i.h + Internal structures for TLSv1 client + +tlsv1_client_read.c + TLSv1 client: read handshake messages + +tlsv1_client_write.c + TLSv1 client: write handshake messages + +tlsv1_common.c and tlsv1_common.h + Common TLSv1 routines and definitions + +tlsv1_cred.c and tlsv1_cred.h + TLSv1 credentials + +tlsv1_record.c and tlsv1_record.h + TLSv1 record protocol + + +\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) + +config_winreg.c + Configuration backend for Windows registry + + +\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 + +ctrl_iface_unix.c + UNIX domain sockets -based control interface backend + +ctrl_iface_udp.c + UDP sockets -based control interface backend + +ctrl_iface_named_pipe.c + Windows named pipes -based control interface backend + +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_sake.c, eap_sake_common.h, eap_sake_common.c + EAP-SAKE + +eap_gpsk.c, eap_gpsk_common.h, eap_gpsk_common.c + EAP-GPSK + +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_supp_sm.c and eapol_supp_sm.h + EAPOL supplicant state machine and IEEE 802.1X processing + + +\section win_port Windows port + +ndis_events.c + Code for receiving NdisMIndicateStatus() events and delivering them to + %wpa_supplicant driver_ndis.c 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 + +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/wpa_supplicant/doc/ctrl_iface.doxygen b/wpa_supplicant/doc/ctrl_iface.doxygen new file mode 100644 index 0000000000000..e908e0f97b011 --- /dev/null +++ b/wpa_supplicant/doc/ctrl_iface.doxygen @@ -0,0 +1,481 @@ +/** +\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 +id=0 +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_RECONNECT RECONNECT + +Connect if disconnected (i.e., like \c REASSOCIATE, but only connect +if in disconnected state). + + +\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 or \c RECONNECT 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_BSS BSS + +Get detailed per-BSS scan results. \c BSS command can be used to +iterate through scan results one BSS at a time and to fetch all +information from the found BSSes. This provides access to the same +data that is available through \c SCAN_RESULTS but in a way that +avoids problems with large number of scan results not fitting in the +ctrl_iface messages. + +There are two options for selecting the BSS with the \c BSS command: +"BSS <idx>" requests information for the BSS identified by the index +(0 .. size-1) in the scan results table and "BSS <BSSID>" requests +information for the given BSS (based on BSSID in 00:01:02:03:04:05 +format). + +BSS information is presented in following format. Please note that new +fields may be added to this field=value data, so the ctrl_iface user +should be prepared to ignore values it does not understand. + +\verbatim +bssid=00:09:5b:95:e0:4e +freq=2412 +beacon_int=0 +capabilities=0x0011 +qual=51 +noise=161 +level=212 +tsf=0000000000000000 +ie=000b6a6b6d2070726976617465010180dd180050f20101000050f20401000050f20401000050f2020000 +ssid=jkm private +\endverbatim + + + +\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. Special network id \c all can be +used to enable all network. + + +\subsection ctrl_iface_DISABLE_NETWORK DISABLE_NETWORK <network id> + +Disable a network. Network id can be received from the +\c LIST_NETWORKS command output. Special network id \c all can be +used to disable all network. + + +\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. Special network id \c all can be +used to remove all network. + + +\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> [strict] + +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. +If optional argument, 'strict', is added, only the values that the +driver claims to explicitly support are included. Without this, all +available capabilities are included if the driver does not provide +a mechanism for querying capabilities. + +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 pairwise strict +\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 + + +\subsection ctrl_iface_AP_SCAN AP_SCAN <ap_scan value> + +Change ap_scan value: +0 = no scanning, +1 = %wpa_supplicant requests scans and uses scan results to select the AP, +2 = %wpa_supplicant does not use scanning and just requests driver to +associate and take care of AP selection + + +\subsection ctrl_iface_INTERFACES INTERFACES + +List configured interfaces. + +\verbatim +wlan0 +eth0 +\endverbatim + +*/ diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore new file mode 100644 index 0000000000000..8c3945c526b54 --- /dev/null +++ b/wpa_supplicant/doc/docbook/.gitignore @@ -0,0 +1,6 @@ +manpage.links +manpage.refs +*.8 +*.5 +*.html +*.pdf diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile new file mode 100644 index 0000000000000..aaeee2eee4533 --- /dev/null +++ b/wpa_supplicant/doc/docbook/Makefile @@ -0,0 +1,27 @@ +all: man html pdf + +FILES += wpa_background +FILES += wpa_cli +FILES += wpa_gui +FILES += wpa_passphrase +FILES += wpa_priv +FILES += wpa_supplicant.conf +FILES += wpa_supplicant + +man: + for i in $(FILES); do docbook2man $$i.sgml; done + +html: + for i in $(FILES); do docbook2html $$i.sgml && \ + mv index.html $$i.html; done + +pdf: + for i in $(FILES); do docbook2pdf $$i.sgml; done + + +clean: + rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 + rm -f wpa_supplicant.conf.5 + rm -f manpage.links manpage.refs + rm -f $(FILES:%=%.pdf) + rm -f $(FILES:%=%.html) diff --git a/wpa_supplicant/doc/docbook/manpage.links b/wpa_supplicant/doc/docbook/manpage.links new file mode 100644 index 0000000000000..e69de29bb2d1d --- /dev/null +++ b/wpa_supplicant/doc/docbook/manpage.links diff --git a/wpa_supplicant/doc/docbook/manpage.refs b/wpa_supplicant/doc/docbook/manpage.refs new file mode 100644 index 0000000000000..16ffc791b0e1b --- /dev/null +++ b/wpa_supplicant/doc/docbook/manpage.refs @@ -0,0 +1,4 @@ +{ + '' => '', + '' => '' +} diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 new file mode 100644 index 0000000000000..3bda3f4e50334 --- /dev/null +++ b/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" "15 February 2009" "" "" + +.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-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml new file mode 100644 index 0000000000000..f47235b17118a --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -0,0 +1,101 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_background</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_background</refname> + <refpurpose>Background information on Wi-Fi Protected Access and IEEE 802.11i</refpurpose> + </refnamediv> + <refsect1> + <title>WPA</title> + + <para>The original security mechanism of IEEE 802.11 standard was + not designed to be strong and has proven to be insufficient for + most networks that require some kind of security. Task group I + (Security) of IEEE 802.11 working group + (http://www.ieee802.org/11/) has worked to address the flaws of + the base standard and has in practice completed its work in May + 2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was + approved in June 2004 and published in July 2004.</para> + + <para>Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version + of the IEEE 802.11i work (draft 3.0) to define a subset of the + security enhancements that can be implemented with existing wlan + hardware. This is called Wi-Fi Protected Access<TM> (WPA). This + has now become a mandatory component of interoperability testing + and certification done by Wi-Fi Alliance. Wi-Fi provides + information about WPA at its web site + (http://www.wi-fi.org/OpenSection/protected_access.asp).</para> + + <para>IEEE 802.11 standard defined wired equivalent privacy (WEP) + algorithm for protecting wireless networks. WEP uses RC4 with + 40-bit keys, 24-bit initialization vector (IV), and CRC32 to + protect against packet forgery. All these choices have proven to + be insufficient: key space is too small against current attacks, + RC4 key scheduling is insufficient (beginning of the pseudorandom + stream should be skipped), IV space is too small and IV reuse + makes attacks easier, there is no replay protection, and non-keyed + authentication does not protect against bit flipping packet + data.</para> + + <para>WPA is an intermediate solution for the security issues. It + uses Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP + is a compromise on strong security and possibility to use existing + hardware. It still uses RC4 for the encryption like WEP, but with + per-packet RC4 keys. In addition, it implements replay protection, + keyed packet authentication mechanism (Michael MIC).</para> + + <para>Keys can be managed using two different mechanisms. WPA can + either use an external authentication server (e.g., RADIUS) and + EAP just like IEEE 802.1X is using or pre-shared keys without need + for additional servers. Wi-Fi calls these "WPA-Enterprise" and + "WPA-Personal", respectively. Both mechanisms will generate a + master session key for the Authenticator (AP) and Supplicant + (client station).</para> + + <para>WPA implements a new key handshake (4-Way Handshake and + Group Key Handshake) for generating and exchanging data encryption + keys between the Authenticator and Supplicant. This handshake is + also used to verify that both Authenticator and Supplicant know + the master session key. These handshakes are identical regardless + of the selected key management mechanism (only the method for + generating master session key changes).</para> + </refsect1> + + <refsect1> + <title>IEEE 802.11i / WPA2</title> + + <para>The design for parts of IEEE 802.11i that were not included + in WPA has finished (May 2004) and this amendment to IEEE 802.11 + was approved in June 2004. Wi-Fi Alliance is using the final IEEE + 802.11i as a new version of WPA called WPA2. This includes, e.g., + support for more robust encryption algorithm (CCMP: AES in Counter + mode with CBC-MAC) to replace TKIP and optimizations for handoff + (reduced number of messages in initial key handshake, + pre-authentication, and PMKSA caching).</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 new file mode 100644 index 0000000000000..4e4aa461166c8 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -0,0 +1,210 @@ +.\" 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" "15 February 2009" "" "" + +.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= parameter 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 +\fBidentity\fR, \fBpassword\fR, and +\fBotp\fR commands. <id> needs to be copied from +the matching request. \fBpassword\fR and +\fBotp\fR 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 \fBpassword\fR are +remembered as long as wpa_supplicant is running whereas values given +with \fBotp\fR 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 "CONNECTED" or "DISCONNECTED" depending on the event. +This can be used to execute networking tools required to configure +the interface. + +Additionally, three environmental variables are available to +the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR +contains the absolute path to the ctrl_interface socket. WPA_ID +contains the unique network_id identifier assigned to the active +network, and WPA_ID_STR contains the content of the id_str option. +.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 <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-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml new file mode 100644 index 0000000000000..1fe98f4fc56ea --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -0,0 +1,339 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_cli</refname> + + <refpurpose>WPA command line client</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_cli</command> + <arg>-p <replaceable>path to ctrl sockets</replaceable></arg> + <arg>-i <replaceable>ifname</replaceable></arg> + <arg>-hvB</arg> + <arg>-a <replaceable>action file</replaceable></arg> + <arg>-P <replaceable>pid file</replaceable></arg> + <arg><replaceable>command ...</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para>wpa_cli is a text-based frontend program for interacting + with wpa_supplicant. It is used to query current status, change + configuration, trigger events, and request interactive user + input.</para> + + <para>wpa_cli can show the current authentication status, selected + security mode, dot11 and dot1x MIBs, etc. In addition, it can + configure some variables like EAPOL state machine parameters and + trigger events like reassociation and IEEE 802.1X + logoff/logon. wpa_cli provides a user interface to request + authentication information, like username and password, if these + are not included in the configuration. This can be used to + implement, e.g., one-time-passwords or generic token card + authentication where the authentication is based on a + challenge-response that uses an external device for generating the + response.</para> + + <para>The control interface of wpa_supplicant can be configured to + allow non-root user access (ctrl_interface GROUP= parameter in the + configuration file). This makes it possible to run wpa_cli with a + normal user account.</para> + + <para>wpa_cli supports two modes: interactive and command + line. Both modes share the same command set and the main + difference is in interactive mode providing access to unsolicited + messages (event messages, username/password requests).</para> + + <para>Interactive mode is started when wpa_cli is executed without + including the command as a command line parameter. Commands are + then entered on the wpa_cli prompt. In command line mode, the same + commands are entered as command line arguments for wpa_cli.</para> + </refsect1> + <refsect1> + <title>Interactive authentication parameters request</title> + + <para>When wpa_supplicant need authentication parameters, like + username and password, which are not present in the configuration + file, it sends a request message to all attached frontend programs, + e.g., wpa_cli in interactive mode. wpa_cli shows these requests + with "CTRL-REQ-<type>-<id>:<text>" + prefix. <type> is IDENTITY, PASSWORD, or OTP + (one-time-password). <id> is a unique identifier for the + current network. <text> is description of the request. In + case of OTP request, it includes the challenge from the + authentication server.</para> + + <para>The reply to these requests can be given with + <emphasis>identity</emphasis>, <emphasis>password</emphasis>, and + <emphasis>otp</emphasis> commands. <id> needs to be copied from + the matching request. <emphasis>password</emphasis> and + <emphasis>otp</emphasis> commands can be used regardless of whether + the request was for PASSWORD or OTP. The main difference between these + two commands is that values given with <emphasis>password</emphasis> are + remembered as long as wpa_supplicant is running whereas values given + with <emphasis>otp</emphasis> are used only once and then forgotten, + i.e., wpa_supplicant will ask frontend for a new value for every use. + This can be used to implement one-time-password lists and generic token + card -based authentication.</para> + + <para>Example request for password and a matching reply:</para> + +<blockquote><programlisting> +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword +</programlisting></blockquote> + + <para>Example request for generic token card challenge-response:</para> + +<blockquote><programlisting> +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 +</programlisting></blockquote> + + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-p path</term> + + <listitem><para>Change the path where control sockets should + be found.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + + <listitem><para>Specify the interface that is being + configured. By default, choose the first interface found with + a control socket in the socket path.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-h</term> + <listitem><para>Help. Show a usage message.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>-v</term> + <listitem><para>Show version information.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>-B</term> + <listitem><para>Run as a daemon in the background.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-a file</term> + + <listitem><para>Run in daemon mode executing the action file + based on events from wpa_supplicant. The specified file will + be executed with the first argument set to interface name and + second to "CONNECTED" or "DISCONNECTED" depending on the event. + This can be used to execute networking tools required to configure + the interface.</para> + + <para>Additionally, three environmental variables are available to + the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR + contains the absolute path to the ctrl_interface socket. WPA_ID + contains the unique network_id identifier assigned to the active + network, and WPA_ID_STR contains the content of the id_str option. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-P file</term> + + <listitem><para>Set the location of the PID + file.</para></listitem> + </varlistentry> + + <varlistentry> + <term>command</term> + + <listitem><para>Run a command. The available commands are + listed in the next section.</para></listitem> + + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>Commands</title> + <para>The following commands are available:</para> + + <variablelist> + <varlistentry> + <term>status</term> + <listitem> + <para>get current WPA/EAPOL/EAP status</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>mib</term> + <listitem> + <para>get MIB variables (dot1x, dot11)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>help</term> + <listitem> + <para>show this usage help</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>interface [ifname]</term> + <listitem> + <para>show interfaces/select interface</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>level <debug level></term> + <listitem> + <para>change debug level</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>license</term> + <listitem> + <para>show full wpa_cli license</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>logoff</term> + <listitem> + <para>IEEE 802.1X EAPOL state machine logoff</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>logon</term> + <listitem> + <para>IEEE 802.1X EAPOL state machine logon</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>set</term> + <listitem> + <para>set variables (shows list of variables when run without arguments)</para> + </listitem> + </varlistentry> + <varlistentry> + <term>pmksa</term> + <listitem> + <para>show PMKSA cache</para> + </listitem> + </varlistentry> + <varlistentry> + <term>reassociate</term> + <listitem> + <para>force reassociation</para> + </listitem> + </varlistentry> + <varlistentry> + <term>reconfigure</term> + <listitem> + <para>force wpa_supplicant to re-read its configuration file</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>preauthenticate <BSSID></term> + <listitem> + <para>force preauthentication</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>identity <network id> <identity></term> + <listitem> + <para>configure identity for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>password <network id> <password></term> + <listitem> + <para>configure password for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>pin <network id> <pin></term> + <listitem> + <para>configure pin for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>otp <network id> <password></term> + <listitem> + <para>configure one-time-password for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>bssid <network id> <BSSID></term> + <listitem> + <para>set preferred BSSID for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>list_networks</term> + <listitem> + <para>list configured networks</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>terminate</term> + <listitem> + <para>terminate <command>wpa_supplicant</command></para> + </listitem> + </varlistentry> + + <varlistentry> + <term>quit</term> + <listitem><para>exit wpa_cli</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 new file mode 100644 index 0000000000000..2f4f638d28b1f --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_gui.8 @@ -0,0 +1,51 @@ +.\" 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_GUI" "8" "15 February 2009" "" "" + +.SH NAME +wpa_gui \- WPA Graphical User Interface +.SH SYNOPSIS + +\fBwpa_gui\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-t\fR ] + +.SH "OVERVIEW" +.PP +wpa_gui is a QT graphical frontend program for interacting +with wpa_supplicant. It is used to query current status, change +configuration and request interactive user input. +.PP +wpa_gui supports (almost) all of the interactive status and +configuration features of the command line client, wpa_cli. Refer +to the wpa_cli manpage for a comprehensive list of the +interactive mode features. +.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-t\fR +Start program in the system tray only (if the window +manager supports it). By default the main status window is +shown. +.SH "SEE ALSO" +.PP +\fBwpa_cli\fR(8) +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml new file mode 100644 index 0000000000000..41b58492440e8 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -0,0 +1,85 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_gui</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_gui</refname> + + <refpurpose>WPA Graphical User Interface</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_gui</command> + <arg>-p <replaceable>path to ctrl sockets</replaceable></arg> + <arg>-i <replaceable>ifname</replaceable></arg> + <arg>-t</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para>wpa_gui is a QT graphical frontend program for interacting + with wpa_supplicant. It is used to query current status, change + configuration and request interactive user input.</para> + + <para>wpa_gui supports (almost) all of the interactive status and + configuration features of the command line client, wpa_cli. Refer + to the wpa_cli manpage for a comprehensive list of the + interactive mode features.</para> + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-p path</term> + + <listitem><para>Change the path where control sockets should + be found.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + + <listitem><para>Specify the interface that is being + configured. By default, choose the first interface found with + a control socket in the socket path.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-t</term> + + <listitem><para>Start program in the system tray only (if the window + manager supports it). By default the main status window is + shown.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 new file mode 100644 index 0000000000000..b123daad80912 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -0,0 +1,40 @@ +.\" 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" "15 February 2009" "" "" + +.SH NAME +wpa_passphrase \- Generate a WPA PSK from an ASCII 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. An ASCII passphrase +and SSID are used to generate a 256-bit PSK. +.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-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml new file mode 100644 index 0000000000000..402ea091f9af4 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -0,0 +1,73 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_passphrase</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_passphrase</refname> + <refpurpose>Generate a WPA PSK from an ASCII passphrase for a SSID</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_passphrase</command> + <arg><replaceable>ssid</replaceable></arg> + <arg><replaceable>passphrase</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para><command>wpa_passphrase</command> pre-computes PSK entries for + network configuration blocks of a + <filename>wpa_supplicant.conf</filename> file. An ASCII passphrase + and SSID are used to generate a 256-bit PSK.</para> + </refsect1> + + <refsect1> + <title>Options</title> + <variablelist> + <varlistentry> + <term>ssid</term> + <listitem> + <para>The SSID whose passphrase should be derived.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>passphrase</term> + <listitem> + <para>The passphrase to use. If not included on the command line, + passphrase will be read from standard input.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 new file mode 100644 index 0000000000000..2191cec94e9ca --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_priv.8 @@ -0,0 +1,120 @@ +.\" 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_PRIV" "8" "15 February 2009" "" "" + +.SH NAME +wpa_priv \- wpa_supplicant privilege separation helper +.SH SYNOPSIS + +\fBwpa_priv\fR [ \fB-c \fIctrl path\fB\fR ] [ \fB-Bdd\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fBdriver:ifname \fI[driver:ifname ...]\fB\fR ] + +.SH "OVERVIEW" +.PP +\fBwpa_priv\fR is a privilege separation helper that +minimizes the size of \fBwpa_supplicant\fR code that needs +to be run with root privileges. +.PP +If enabled, privileged operations are done in the wpa_priv process +while leaving rest of the code (e.g., EAP authentication and WPA +handshakes) to operate in an unprivileged process (wpa_supplicant) that +can be run as non-root user. Privilege separation restricts the effects +of potential software errors by containing the majority of the code in an +unprivileged process to avoid the possibility of a full system +compromise. +.PP +\fBwpa_priv\fR needs to be run with network admin +privileges (usually, root user). It opens a UNIX domain socket for each +interface that is included on the command line; any other interface will +be off limits for \fBwpa_supplicant\fR in this kind of +configuration. After this, \fBwpa_supplicant\fR can be run as +a non-root user (e.g., all standard users on a laptop or as a special +non-privileged user account created just for this purpose to limit access +to user files even further). +.SH "EXAMPLE CONFIGURATION" +.PP +The following steps are an example of how to configure +\fBwpa_priv\fR to allow users in the +\fBwpapriv\fR group to communicate with +\fBwpa_supplicant\fR with privilege separation: +.PP +Create user group (e.g., wpapriv) and assign users that +should be able to use wpa_supplicant into that group. +.PP +Create /var/run/wpa_priv directory for UNIX domain sockets and +control user access by setting it accessible only for the wpapriv +group: +.sp +.RS + +.nf +mkdir /var/run/wpa_priv +chown root:wpapriv /var/run/wpa_priv +chmod 0750 /var/run/wpa_priv +.fi +.RE +.PP +Start \fBwpa_priv\fR as root (e.g., from system +startup scripts) with the enabled interfaces configured on the +command line: +.sp +.RS + +.nf +wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0 +.fi +.RE +.PP +Run \fBwpa_supplicant\fR as non-root with a user +that is in the wpapriv group: +.sp +.RS + +.nf +wpa_supplicant -i ath0 -c wpa_supplicant.conf +.fi +.RE +.SH "COMMAND ARGUMENTS" +.TP +\fB-c ctrl path\fR +Specify the path to wpa_priv control directory +(Default: /var/run/wpa_priv/). +.TP +\fB-B\fR +Run as a daemon in the background. +.TP +\fB-P file\fR +Set the location of the PID +file. +.TP +\fBdriver:ifname [driver:ifname ...]\fR +The <driver> string dictates which of the +supported \fBwpa_supplicant\fR driver backends is to be +used. To get a list of supported driver types see wpa_supplicant help +(e.g, wpa_supplicant -h). The driver backend supported by most good +drivers is \fBwext\fR\&. + +The <ifname> string specifies which network +interface is to be managed by \fBwpa_supplicant\fR +(e.g., wlan0 or ath0). + +\fBwpa_priv\fR does not use the network interface +before \fBwpa_supplicant\fR is started, so it is fine to +include network interfaces that are not available at the time wpa_priv +is started. wpa_priv can control multiple interfaces with one process, +but it is also possible to run multiple \fBwpa_priv\fR +processes at the same time, if desired. +.SH "SEE ALSO" +.PP +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml new file mode 100644 index 0000000000000..89b8a92219657 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -0,0 +1,148 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_priv</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_priv</refname> + + <refpurpose>wpa_supplicant privilege separation helper</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_priv</command> + <arg>-c <replaceable>ctrl path</replaceable></arg> + <arg>-Bdd</arg> + <arg>-P <replaceable>pid file</replaceable></arg> + <arg>driver:ifname <replaceable>[driver:ifname ...]</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para><command>wpa_priv</command> is a privilege separation helper that + minimizes the size of <command>wpa_supplicant</command> code that needs + to be run with root privileges.</para> + + <para>If enabled, privileged operations are done in the wpa_priv process + while leaving rest of the code (e.g., EAP authentication and WPA + handshakes) to operate in an unprivileged process (wpa_supplicant) that + can be run as non-root user. Privilege separation restricts the effects + of potential software errors by containing the majority of the code in an + unprivileged process to avoid the possibility of a full system + compromise.</para> + + <para><command>wpa_priv</command> needs to be run with network admin + privileges (usually, root user). It opens a UNIX domain socket for each + interface that is included on the command line; any other interface will + be off limits for <command>wpa_supplicant</command> in this kind of + configuration. After this, <command>wpa_supplicant</command> can be run as + a non-root user (e.g., all standard users on a laptop or as a special + non-privileged user account created just for this purpose to limit access + to user files even further).</para> + </refsect1> + <refsect1> + <title>Example configuration</title> + + <para>The following steps are an example of how to configure + <command>wpa_priv</command> to allow users in the + <emphasis>wpapriv</emphasis> group to communicate with + <command>wpa_supplicant</command> with privilege separation:</para> + + <para>Create user group (e.g., wpapriv) and assign users that + should be able to use wpa_supplicant into that group.</para> + + <para>Create /var/run/wpa_priv directory for UNIX domain sockets and + control user access by setting it accessible only for the wpapriv + group:</para> + +<blockquote><programlisting> +mkdir /var/run/wpa_priv +chown root:wpapriv /var/run/wpa_priv +chmod 0750 /var/run/wpa_priv +</programlisting></blockquote> + + <para>Start <command>wpa_priv</command> as root (e.g., from system + startup scripts) with the enabled interfaces configured on the + command line:</para> + +<blockquote><programlisting> +wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0 +</programlisting></blockquote> + + <para>Run <command>wpa_supplicant</command> as non-root with a user + that is in the wpapriv group:</para> + +<blockquote><programlisting> +wpa_supplicant -i ath0 -c wpa_supplicant.conf +</programlisting></blockquote> + + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-c ctrl path</term> + + <listitem><para>Specify the path to wpa_priv control directory + (Default: /var/run/wpa_priv/).</para></listitem> + </varlistentry> + + <varlistentry> + <term>-B</term> + <listitem><para>Run as a daemon in the background.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-P file</term> + + <listitem><para>Set the location of the PID + file.</para></listitem> + </varlistentry> + + <varlistentry> + <term>driver:ifname [driver:ifname ...]</term> + + <listitem><para>The <driver> string dictates which of the + supported <command>wpa_supplicant</command> driver backends is to be + used. To get a list of supported driver types see wpa_supplicant help + (e.g, wpa_supplicant -h). The driver backend supported by most good + drivers is <emphasis>wext</emphasis>.</para> + + <para>The <ifname> string specifies which network + interface is to be managed by <command>wpa_supplicant</command> + (e.g., wlan0 or ath0).</para> + + <para><command>wpa_priv</command> does not use the network interface + before <command>wpa_supplicant</command> is started, so it is fine to + include network interfaces that are not available at the time wpa_priv + is started. wpa_priv can control multiple interfaces with one process, + but it is also possible to run multiple <command>wpa_priv</command> + processes at the same time, if desired.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 new file mode 100644 index 0000000000000..0106c69697f07 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -0,0 +1,571 @@ +.\" 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" "15 February 2009" "" "" + +.SH NAME +wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant +.SH SYNOPSIS + +\fBwpa_supplicant\fR [ \fB-BddfhKLqqtuvW\fR ] [ \fB-i\fIifname\fB\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-D\fIdriver\fB\fR ] [ \fB-P\fIPID_file\fB\fR ] [ \fB-f\fIoutput file\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 be +loaded. 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 +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 +A summary of available driver backends is below. Support for each +of the driver backends is chosen at wpa_supplicant compile time. For a +list of supported driver backends that may be used with the -D option on +your system, refer to the help output of wpa_supplicant +(\fBwpa_supplicant -h\fR). +.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 +\fBroboswitch\fR +wpa_supplicant Broadcom switch driver +.TP +\fBbsd\fR +BSD 802.11 support (Atheros, etc.). +.TP +\fBndis\fR +Windows NDIS driver. +.SH "COMMAND LINE OPTIONS" +.PP +Most command line options have global scope. Some are given per +interface, and are only valid if at least one \fB-i\fR option +is specified, otherwise they're ignored. Option groups for different +interfaces must be separated by \fB-N\fR option. +.TP +\fB-b br_ifname\fR +Optional bridge interface name. (Per interface) +.TP +\fB-B\fR +Run daemon in the background. +.TP +\fB-c filename\fR +Path to configuration file. (Per interface) +.TP +\fB-C ctrl_interface\fR +Path to ctrl_interface socket (Per interface. Only used if +\fB-c\fR is not). +.TP +\fB-i ifname\fR +Interface to listen on. Multiple instances of this option can +be present, one per interface, separated by \fB-N\fR +option (see below). +.TP +\fB-d\fR +Increase debugging verbosity (\fB-dd\fR even +more). +.TP +\fB-D driver\fR +Driver to use. (Per interface, see the available options +below.) +.TP +\fB-f output file\fR +Log output to specified file instead of stdout. +.TP +\fB-g global ctrl_interface\fR +Path to global ctrl_interface socket. If specified, interface +definitions may be omitted. +.TP +\fB-K\fR +Include keys (passwords, etc.) in debug output. +.TP +\fB-t\fR +Include timestamp in debug messages. +.TP +\fB-h\fR +Help. Show a usage message. +.TP +\fB-L\fR +Show license (GPL and BSD). +.TP +\fB-p\fR +Driver parameters. (Per interface) +.TP +\fB-P PID_file\fR +Path to PID file. +.TP +\fB-q\fR +Decrease debugging verbosity (\fB-qq\fR even +less). +.TP +\fB-u\fR +Enabled DBus control interface. If enabled, interface +definitions may be omitted. +.TP +\fB-v\fR +Show version. +.TP +\fB-W\fR +Wait for a control interface monitor before starting. +.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 -B -c/etc/wpa_supplicant.conf -iwlan0 +.fi +.RE +.PP +This makes the process fork into background. +.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 (\fBiwconfig wlan0 mode managed\fR). +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.conf\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 \fBstart\fR +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 -B -c/etc/wpa_supplicant.conf -i$DEVICE +fi + +.fi +.RE +.PP +Add the following block to the end of \fBstop\fR +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. +.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-2007, +Jouni Malinen <j@w1.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/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 new file mode 100644 index 0000000000000..7a01ea2f5d310 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -0,0 +1,225 @@ +.\" 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" "15 February 2009" "" "" + +.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 +the \fBwpa_cli reconfigure\fR 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 network based on the order of network blocks in +the configuration file, network security level (WPA/WPA2 is +preferred), 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=DIR=/var/run/wpa_supplicant GROUP=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} +.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=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} +.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=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} +.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=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} +.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=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} +.fi +.RE +.TP 3 +6. +Authentication for wired Ethernet. This can be used with +\fBwired\fR or \fBroboswitch\fR interface +(-Dwired or -Droboswitch on command line). +.sp +.RS + +.nf +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +ap_scan=0 +network={ + key_mgmt=IEEE8021X + eap=MD5 + identity="user" + password="password" + eapol_flags=0 +} +.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/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml new file mode 100644 index 0000000000000..462039d9ed933 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml @@ -0,0 +1,239 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> +<refentry> + <refmeta> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_supplicant.conf</refname> + <refpurpose>configuration file for wpa_supplicant</refpurpose> + </refnamediv> + <refsect1> + <title>Overview</title> + + <para><command>wpa_supplicant</command> is configured using a text + file that lists all accepted networks and security policies, + including pre-shared keys. See the example configuration file, + probably in <command>/usr/share/doc/wpa_supplicant/</command>, for + detailed information about the configuration format and supported + fields.</para> + + <para>All file paths in this configuration file should use full + (absolute, not relative to working directory) path in order to allow + working directory to be changed. This can happen if wpa_supplicant is + run in the background.</para> + + <para>Changes to configuration file can be reloaded be sending + SIGHUP signal to <command>wpa_supplicant</command> ('killall -HUP + wpa_supplicant'). Similarly, reloading can be triggered with + the <emphasis>wpa_cli reconfigure</emphasis> command.</para> + + <para>Configuration file can include one or more network blocks, + e.g., one for each used SSID. wpa_supplicant will automatically + select the best network based on the order of network blocks in + the configuration file, network security level (WPA/WPA2 is + preferred), and signal strength.</para> + </refsect1> + + <refsect1> + <title>Quick Examples</title> + + <orderedlist> + <listitem> + + <para>WPA-Personal (PSK) as home network and WPA-Enterprise with + EAP-TLS as work network.</para> + +<blockquote><programlisting> +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that + use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse + Aegis, Interlink RAD-Series)</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-MD5-Challenge configuration with anonymous + identity for the unencrypted use. Real identity is sent only + within an encrypted TLS tunnel.</para> + + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} +</programlisting></blockquote> + + </listitem> + + <listitem> + <para>IEEE 802.1X (i.e., no WPA) with dynamic WEP keys + (require both unicast and broadcast); use EAP-TLS for + authentication</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} +</programlisting></blockquote> + </listitem> + + + <listitem> + <para>Catch all example that allows more or less all + configuration modes. The configuration options are used based + on what security policy is used in the selected SSID. This is + mostly for testing and is not recommended for normal + use.</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>Authentication for wired Ethernet. This can be used with + <emphasis>wired</emphasis> or <emphasis>roboswitch</emphasis> interface + (-Dwired or -Droboswitch on command line).</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +ap_scan=0 +network={ + key_mgmt=IEEE8021X + eap=MD5 + identity="user" + password="password" + eapol_flags=0 +} +</programlisting></blockquote> + </listitem> + </orderedlist> + + + + + + </refsect1> + <refsect1> + <title>Certificates</title> + + <para>Some EAP authentication methods require use of + certificates. EAP-TLS uses both server side and client + certificates whereas EAP-PEAP and EAP-TTLS only require the server + side certificate. When client certificate is used, a matching + private key file has to also be included in configuration. If the + private key uses a passphrase, this has to be configured in + wpa_supplicant.conf ("private_key_passwd").</para> + + <para>wpa_supplicant supports X.509 certificates in PEM and DER + formats. User certificate and private key can be included in the + same file.</para> + + <para>If the user certificate and private key is received in + PKCS#12/PFX format, they need to be converted to suitable PEM/DER + format for wpa_supplicant. This can be done, e.g., with following + commands:</para> +<blockquote><programlisting> +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys +</programlisting></blockquote> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>openssl</refentrytitle> + <manvolnum>1</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml new file mode 100644 index 0000000000000..9798cedf16a3d --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -0,0 +1,818 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_supplicant</refname> + <refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_supplicant</command> + <arg>-BddfhKLqqtuvW</arg> + <arg>-i<replaceable>ifname</replaceable></arg> + <arg>-c<replaceable>config file</replaceable></arg> + <arg>-D<replaceable>driver</replaceable></arg> + <arg>-P<replaceable>PID_file</replaceable></arg> + <arg>-f<replaceable>output file</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + <refsect1> + <title>Overview</title> + + <para> + Wireless networks do not require physical access to the network equipment + in the same way as wired networks. This makes it easier for unauthorized + users to passively monitor a network and capture all transmitted frames. + In addition, unauthorized use of the network is much easier. In many cases, + this can happen even without user's explicit knowledge since the wireless + LAN adapter may have been configured to automatically join any available + network. + </para> + + <para> + Link-layer encryption can be used to provide a layer of security for + wireless networks. The original wireless LAN standard, IEEE 802.11, + included a simple encryption mechanism, WEP. However, that proved to + be flawed in many areas and network protected with WEP cannot be consider + secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys + can be used to improve the network security, but even that has inherited + security issues due to the use of WEP for encryption. Wi-Fi Protected + Access and IEEE 802.11i amendment to the wireless LAN standard introduce + a much improvement mechanism for securing wireless networks. IEEE 802.11i + enabled networks that are using CCMP (encryption mechanism based on strong + cryptographic algorithm AES) can finally be called secure used for + applications which require efficient protection against unauthorized + access. + </para> + + <para><command>wpa_supplicant</command> is an implementation of + the WPA Supplicant component, i.e., the part that runs in the + client stations. It implements WPA key negotiation with a WPA + Authenticator and EAP authentication with Authentication + Server. In addition, it controls the roaming and IEEE 802.11 + authentication/association of the wireless LAN driver.</para> + + <para><command>wpa_supplicant</command> is designed to be a + "daemon" program that runs in the background and acts as the + backend component controlling the wireless + connection. <command>wpa_supplicant</command> supports separate + frontend programs and an example text-based frontend, + <command>wpa_cli</command>, is included with + wpa_supplicant.</para> + + <para>Before wpa_supplicant can do its work, the network interface + must be available. That means that the physical device must be + present and enabled, and the driver for the device must be + loaded. The daemon will exit immediately if the device is not already + available.</para> + + <para>After <command>wpa_supplicant</command> has configured the + network device, higher level configuration such as DHCP may + proceed. There are a variety of ways to integrate wpa_supplicant + into a machine's networking scripts, a few of which are described + in sections below.</para> + + <para>The following steps are used when associating with an AP + using WPA:</para> + + <itemizedlist> + <listitem> + <para><command>wpa_supplicant</command> requests the kernel + driver to scan neighboring BSSes</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> selects a BSS based on + its configuration</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> requests the kernel + driver to associate with the chosen BSS</para> + </listitem> + + <listitem> + <para>If WPA-EAP: integrated IEEE 802.1X Supplicant + completes EAP authentication with the + authentication server (proxied by the Authenticator in the + AP)</para> + </listitem> + + <listitem> + <para>If WPA-EAP: master key is received from the IEEE 802.1X + Supplicant</para> + </listitem> + + <listitem> + <para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK + as the master session key</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> completes WPA 4-Way + Handshake and Group Key Handshake with the Authenticator + (AP)</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> configures encryption + keys for unicast and broadcast</para> + </listitem> + + <listitem> + <para>normal data packets can be transmitted and received</para> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Supported Features</title> + <para>Supported WPA/IEEE 802.11i features:</para> + <itemizedlist> + <listitem> + <para>WPA-PSK ("WPA-Personal")</para> + </listitem> + + <listitem> + <para>WPA with EAP (e.g., with RADIUS authentication server) + ("WPA-Enterprise") Following authentication methods are + supported with an integrate IEEE 802.1X Supplicant:</para> + + <itemizedlist> + <listitem> + <para>EAP-TLS</para> + </listitem> + </itemizedlist> + + <itemizedlist> + <listitem> + <para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para> + </listitem> + + + <listitem> + <para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-MD5-Challenge</para> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-GTC</para> + </listitem> + + <listitem><para>EAP-TTLS/EAP-OTP</para></listitem> + + <listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem> + + <listitem><para>EAP-TTLS/EAP-TLS</para></listitem> + + <listitem><para>EAP-TTLS/MSCHAPv2</para></listitem> + + <listitem><para>EAP-TTLS/MSCHAP</para></listitem> + + <listitem><para>EAP-TTLS/PAP</para></listitem> + + <listitem><para>EAP-TTLS/CHAP</para></listitem> + + <listitem><para>EAP-SIM</para></listitem> + + <listitem><para>EAP-AKA</para></listitem> + + <listitem><para>EAP-PSK</para></listitem> + + <listitem><para>EAP-PAX</para></listitem> + + <listitem><para>LEAP (note: requires special support from + the driver for IEEE 802.11 authentication)</para></listitem> + + <listitem><para>(following methods are supported, but since + they do not generate keying material, they cannot be used + with WPA or IEEE 802.1X WEP keying)</para></listitem> + + <listitem><para>EAP-MD5-Challenge </para></listitem> + + <listitem><para>EAP-MSCHAPv2</para></listitem> + + <listitem><para>EAP-GTC</para></listitem> + + <listitem><para>EAP-OTP</para></listitem> + </itemizedlist> + </listitem> + + <listitem> + <para>key management for CCMP, TKIP, WEP104, WEP40</para> + </listitem> + + <listitem> + <para>RSN/WPA2 (IEEE 802.11i)</para> + <itemizedlist> + <listitem> + <para>pre-authentication</para> + </listitem> + + <listitem> + <para>PMKSA caching</para> + </listitem> + </itemizedlist> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Available Drivers</title> + <para>A summary of available driver backends is below. Support for each + of the driver backends is chosen at wpa_supplicant compile time. For a + list of supported driver backends that may be used with the -D option on + your system, refer to the help output of wpa_supplicant + (<emphasis>wpa_supplicant -h</emphasis>).</para> + + <variablelist> + <varlistentry> + <term>hostap</term> + <listitem> + <para>(default) Host AP driver (Intersil Prism2/2.5/3). + (this can also be used with Linuxant DriverLoader).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>hermes</term> + <listitem> + <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>madwifi</term> + <listitem> + <para>MADWIFI 802.11 support (Atheros, etc.).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>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>roboswitch</term> + <listitem> + <para>wpa_supplicant Broadcom switch driver</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>bsd</term> + <listitem> + <para>BSD 802.11 support (Atheros, etc.).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>ndis</term> + <listitem> + <para>Windows NDIS driver.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Command Line Options</title> + <para>Most command line options have global scope. Some are given per + interface, and are only valid if at least one <option>-i</option> option + is specified, otherwise they're ignored. Option groups for different + interfaces must be separated by <option>-N</option> option.</para> + <variablelist> + <varlistentry> + <term>-b br_ifname</term> + <listitem> + <para>Optional bridge interface name. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-B</term> + <listitem> + <para>Run daemon in the background.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-c filename</term> + <listitem> + <para>Path to configuration file. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-C ctrl_interface</term> + <listitem> + <para>Path to ctrl_interface socket (Per interface. Only used if + <option>-c</option> is not).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + <listitem> + <para>Interface to listen on. Multiple instances of this option can + be present, one per interface, separated by <option>-N</option> + option (see below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-d</term> + <listitem> + <para>Increase debugging verbosity (<option>-dd</option> even + more).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-D driver</term> + <listitem> + <para>Driver to use. (Per interface, see the available options + below.)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-f output file</term> + <listitem> + <para>Log output to specified file instead of stdout.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-g global ctrl_interface</term> + <listitem> + <para>Path to global ctrl_interface socket. If specified, interface + definitions may be omitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-K</term> + <listitem> + <para>Include keys (passwords, etc.) in debug output.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-t</term> + <listitem> + <para>Include timestamp in debug messages.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-h</term> + <listitem> + <para>Help. Show a usage message.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-L</term> + <listitem> + <para>Show license (GPL and BSD).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-p</term> + <listitem> + <para>Driver parameters. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-P PID_file</term> + <listitem> + <para>Path to PID file.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-q</term> + <listitem> + <para>Decrease debugging verbosity (<option>-qq</option> even + less).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-u</term> + <listitem> + <para>Enabled DBus control interface. If enabled, interface + definitions may be omitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-v</term> + <listitem> + <para>Show version.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-W</term> + <listitem> + <para>Wait for a control interface monitor before starting.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-N</term> + <listitem> + <para>Start describing new interface.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>In most common cases, <command>wpa_supplicant</command> is + started with:</para> + +<blockquote><programlisting> +wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0 +</programlisting></blockquote> + + <para>This makes the process fork into background.</para> + + <para>The easiest way to debug problems, and to get debug log for + bug reports, is to start <command>wpa_supplicant</command> on + foreground with debugging enabled:</para> + +<blockquote><programlisting> +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d +</programlisting></blockquote> + + <para><command>wpa_supplicant</command> can control multiple + interfaces (radios) either by running one process for each + interface separately or by running just one process and list of + options at command line. Each interface is separated with -N + argument. As an example, following command would start + wpa_supplicant for two interfaces:</para> + +<blockquote><programlisting> +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi +</programlisting></blockquote> + </refsect1> + + <refsect1> + <title>OS Requirements</title> + <para>Current hardware/software requirements:</para> + + <itemizedlist> + <listitem> + <para>Linux kernel 2.4.x or 2.6.x with Linux Wireless + Extensions v15 or newer</para> + </listitem> + + + <listitem> + <para>FreeBSD 6-CURRENT</para> + </listitem> + + <listitem> + <para>Microsoft Windows with WinPcap (at least WinXP, may work + with other versions)</para> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Supported Drivers</title> + <variablelist> + <varlistentry> + <term>Host AP driver for Prism2/2.5/3 (development + snapshot/v0.2.x)</term> + <listitem> + <para> (http://hostap.epitest.fi/) Driver needs to be set in + Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>). + Please note that station firmware version needs to be 1.7.0 or + newer to work in WPA mode.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Linuxant DriverLoader</term> + <listitem> + <para>(http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Agere Systems Inc. Linux Driver</term> + <listitem> + <para> (http://www.agere.com/support/drivers/) Please note + that the driver interface file (driver_hermes.c) and hardware + specific include files are not included in the wpa_supplicant + distribution. You will need to copy these from the source + package of the Agere driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>madwifi driver for cards based on Atheros chip set (ar521x)</term> + <listitem> + <para> (http://sourceforge.net/projects/madwifi/) Please + note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root + directory (CFLAGS += -I../madwifi/wpa line in example + defconfig).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>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.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for details.</para> + + <para>Once the configuration is ready, you can test whether the + configuration works by running <command>wpa_supplicant</command> + with following command to start it on foreground with debugging + enabled:</para> + + <blockquote><programlisting> +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + </programlisting></blockquote> + + <para>Assuming everything goes fine, you can start using following + command to start <command>wpa_supplicant</command> on background + without debugging:</para> + + <blockquote><programlisting> +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + </programlisting></blockquote> + + <para>Please note that if you included more than one driver + interface in the build time configuration (.config), you may need + to specify which interface to use by including -D<driver + name> option on the command line.</para> + + <!-- XXX at this point, the page could include a little script + based on wpa_cli to wait for a connection and then run + dhclient --> + + </refsect1> + + <refsect1> + <title>Interface to pcmcia-cs/cardmrg</title> + + <para>For example, following small changes to pcmcia-cs scripts + can be used to enable WPA support:</para> + + <para>Add MODE="Managed" and WPA="y" to the network scheme in + <filename>/etc/pcmcia/wireless.opts</filename>.</para> + + <para>Add the following block to the end of <emphasis>start</emphasis> + action handler in <filename>/etc/pcmcia/wireless</filename>:</para> + + <blockquote><programlisting> +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE +fi + </programlisting></blockquote> + + + <para>Add the following block to the end of <emphasis>stop</emphasis> + action handler (may need to be separated from other actions) in + <filename>/etc/pcmcia/wireless</filename>:</para> + + <blockquote><programlisting> +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant +fi + </programlisting></blockquote> + + <para>This will make <command>cardmgr</command> start + <command>wpa_supplicant</command> when the card is plugged + in.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_background</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_passphrase</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/doxygen.fast b/wpa_supplicant/doc/doxygen.fast new file mode 100644 index 0000000000000..c6012a9c91ec9 --- /dev/null +++ b/wpa_supplicant/doc/doxygen.fast @@ -0,0 +1,239 @@ +# Doxyfile 1.4.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = wpa_supplicant +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = wpa_supplicant/doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +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 +SEPARATE_MEMBER_PAGES = 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 = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = wpa_supplicant \ + src/common \ + src/crypto \ + src/drivers \ + src/eap_common \ + src/eapol_supp \ + src/eap_peer \ + src/l2_packet \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps +FILE_PATTERNS = *.c *.h *.cpp *.m *.doxygen +RECURSIVE = YES +EXCLUDE = wpa_supplicant/wpa_gui +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.moc/* */.ui/* +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = wpa_supplicant/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 CONFIG_CTRL_IFACE +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_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/wpa_supplicant/doc/doxygen.full b/wpa_supplicant/doc/doxygen.full new file mode 100644 index 0000000000000..6884c62771d0d --- /dev/null +++ b/wpa_supplicant/doc/doxygen.full @@ -0,0 +1,239 @@ +# Doxyfile 1.4.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = wpa_supplicant +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = wpa_supplicant/doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +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 +SEPARATE_MEMBER_PAGES = 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 = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = wpa_supplicant \ + src/common \ + src/crypto \ + src/drivers \ + src/eap_common \ + src/eapol_supp \ + src/eap_peer \ + src/l2_packet \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps +FILE_PATTERNS = *.c *.h *.cpp *.m *.doxygen +RECURSIVE = YES +EXCLUDE = wpa_supplicant/wpa_gui +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.moc/* */.ui/* +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = wpa_supplicant/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 CONFIG_CTRL_IFACE +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_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/wpa_supplicant/doc/driver_wrapper.doxygen b/wpa_supplicant/doc/driver_wrapper.doxygen new file mode 100644 index 0000000000000..28aea50d2ab4f --- /dev/null +++ b/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 driver.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 +driver.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 driver.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/wpa_supplicant/doc/eap.doxygen b/wpa_supplicant/doc/eap.doxygen new file mode 100644 index 0000000000000..0646128a8598d --- /dev/null +++ b/wpa_supplicant/doc/eap.doxygen @@ -0,0 +1,87 @@ +/** +\page eap_module EAP peer implementation + +Extensible Authentication Protocol (EAP) is an authentication framework +defined in RFC 3748. %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 the build +(Makefile) and the EAP method registration list in the +eap_peer_register_methods() function of eap_methods.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. + +It is recommended that the EAP methods would use generic helper +functions, eap_msg_alloc() and eap_hdr_validate() when processing +messages. This allows code sharing and can avoid missing some of the +needed validation steps for received packets. In addition, these +functions make it easier to change between expanded and legacy EAP +header, if needed. + +When adding an EAP method that uses a vendor specific EAP type +(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method +must be registered by passing vendor id instead of EAP_VENDOR_IETF to +eap_peer_method_alloc(). These methods must not try to emulate +expanded types by registering a legacy EAP method for type 254. See +eap_vendor_test.c for an example of an EAP method implementation that +is implemented as an expanded type. + + +\section used_eap_library Using EAP implementation as a library + +The Git repository has an eap_example directory that contains an +example showing how EAP peer and server code from %wpa_supplicant and +hostapd can be used as a library. The example program initializes both +an EAP server and an EAP peer entities and then runs through an +EAP-PEAP/MSCHAPv2 authentication. + +eap_example_peer.c shows the initialization and glue code needed to +control the EAP peer implementation. eap_example_server.c does the +same for EAP server. eap_example.c is an example that ties in both the +EAP server and client parts to allow an EAP authentication to be +shown. + +In this example, the EAP messages are passed between the server and +the peer are passed by direct function calls within the same process. +In practice, server and peer functionalities would likely reside in +separate devices and the EAP messages would be transmitted between the +devices based on an external protocol. For example, in IEEE 802.11 +uses IEEE 802.1X EAPOL state machines to control the transmission of +EAP messages and WiMax supports optional PMK EAP authentication +mechanism that transmits EAP messages as defined in IEEE 802.16e. + +The EAP library links in number of helper functions from src/utils and +src/crypto directories. Most of these are suitable as-is, but it may +be desirable to replace the debug output code in src/utils/wpa_debug.c +by dropping this file from the library and re-implementing the +functions there in a way that better fits in with the main +application. + +*/ diff --git a/wpa_supplicant/doc/kerneldoc2doxygen.pl b/wpa_supplicant/doc/kerneldoc2doxygen.pl new file mode 100755 index 0000000000000..61bc367b4964c --- /dev/null +++ b/wpa_supplicant/doc/kerneldoc2doxygen.pl @@ -0,0 +1,134 @@ +#!/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-2008 Jouni Malinen <j@w1.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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]; + + # wpa_supplicant -> %wpa_supplicant except for struct wpa_supplicant + $t =~ s/struct wpa_supplicant/struct STRUCTwpa_supplicant/sg; + $t =~ s/ wpa_supplicant/ \%wpa_supplicant/sg; + $t =~ s/struct STRUCTwpa_supplicant/struct wpa_supplicant/sg; + + # " * 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/wpa_supplicant/doc/mainpage.doxygen b/wpa_supplicant/doc/mainpage.doxygen new file mode 100644 index 0000000000000..ed63f27e970d4 --- /dev/null +++ b/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 <j@w1.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 Git repository +is available from the project home page at +http://w1.fi/wpa_supplicant/. This developers' documentation +is also available as a PDF file from +http://w1.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/wpa_supplicant/doc/porting.doxygen b/wpa_supplicant/doc/porting.doxygen new file mode 100644 index 0000000000000..03111346603b3 --- /dev/null +++ b/wpa_supplicant/doc/porting.doxygen @@ -0,0 +1,208 @@ +/** +\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, +PalmOS, Windows CE, and Windows Mobile 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 ansi_c_extra Extra functions on top of ANSI C + +%wpa_supplicant is mostly using ANSI C functions that are available on +most targets. However, couple of additional functions that are common +on modern UNIX systems are used. Number of these are listed with +prototypes in common.h (the \verbatim #ifdef CONFIG_ANSI_C_EXTRA \endverbatim +block). These functions may need to be implemented or at least defined +as macros to native functions in the target OS or C library. + +Many of the common ANSI C functions are used through a wrapper +definitions in os.h to allow these to be replaced easily with a +platform specific version in case standard C libraries are not +available. In addition, os.h defines couple of common platform +specific functions that are implemented in os_unix.c for UNIX like +targets and in os_win32.c for Win32 API. If the target platform does +not support either of these examples, a new os_*.c file may need to be +added. + +Unless OS_NO_C_LIB_DEFINES is defined, the standard ANSI C and POSIX +functions are used by defining the os_*() wrappers to use them +directly in order to avoid extra cost in size and speed. If the target +platform needs different versions of the functions, os.h can be +modified to define the suitable macros or alternatively, +OS_NO_C_LIB_DEFINES may be defined for the build and the wrapper +functions can then be implemented in a new os_*.c wrapper file. + +common.h defines number of helper macros for handling integers of +different size and byte order. Suitable version of these definitions +may need to be added for the target platform. + + +\section configuration_backend Configuration backend + +%wpa_supplicant implements a configuration interface that allows the +backend to be easily replaced in order to read configuration data from +a suitable source depending on the target platform. config.c +implements the generic code that can be shared with all configuration +backends. Each backend is implemented in its own config_*.c file. + +The included config_file.c backend uses a text file for configuration +and config_winreg.c uses Windows registry. These files can be used as +an example for a new configuration backend if the target platform uses +different mechanism for configuration parameters. In addition, +config_none.c can be used as an empty starting point for building a +new configuration backend. + + +\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. + + +\section simple_build Simple build example + +One way to start a porting project is to begin with a very simple +build of %wpa_supplicant with WPA-PSK support and once that is +building correctly, start adding features. + +Following command can be used to build very simple version of +%wpa_supplicant: + +\verbatim +cc -o wpa_supplicant config.c eloop.c common.c md5.c rc4.c sha1.c \ + config_none.c l2_packet_none.c tls_none.c wpa.c preauth.c \ + aes_wrap.c wpa_supplicant.c events.c main_none.c drivers.c +\endverbatim + +The end result is not really very useful since it uses empty functions +for configuration parsing and layer 2 packet access and does not +include a driver interface. However, this is a good starting point +since the build is complete in the sense that all functions are +present and this is easy to configure to a build system by just +including the listed C files. + +Once this version can be build successfully, the end result can be +made functional by adding a proper program entry point (main*.c), +driver interface (driver_*.c and matching CONFIG_DRIVER_* define for +registration in drivers.c), configuration parser/writer (config_*.c), +and layer 2 packet access implementation (l2_packet_*.c). After these +components have been added, the end result should be a working +WPA/WPA2-PSK enabled supplicant. + +After the basic functionality has been verified to work, more features +can be added by linking in more files and defining C pre-processor +defines. Currently, the best source of information for what options +are available and which files needs to be included is in the Makefile +used for building the supplicant with make. Similar configuration will +be needed for build systems that either use different type of make +tool or a GUI-based project configuration. + +*/ diff --git a/wpa_supplicant/doc/testing_tools.doxygen b/wpa_supplicant/doc/testing_tools.doxygen new file mode 100644 index 0000000000000..a2ae0c250fdda --- /dev/null +++ b/wpa_supplicant/doc/testing_tools.doxygen @@ -0,0 +1,295 @@ +/** +\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 [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] [-s<AS secret>] \ + [-r<count>] [-t<timeout>] [-C<Connect-Info>] \ + [-M<client MAC address>] +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 + -S = save configuration after authentiation + -n = no MPPE keys expected + -t<timeout> = sets timeout in seconds (default: 30 s) + -C<Connect-Info> = RADIUS Connect-Info (default: CONNECT 11Mbps 802.11b) + -M<client MAC address> = Set own MAC address (Calling-Station-Id, + default: 02:00:00:00:00:01) +\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/wpa_supplicant/doc/wpa_supplicant.fig b/wpa_supplicant/doc/wpa_supplicant.fig new file mode 100644 index 0000000000000..06abfb54b61bb --- /dev/null +++ b/wpa_supplicant/doc/wpa_supplicant.fig @@ -0,0 +1,247 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +6 1875 4050 2925 4350 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050 +4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001 +-6 +6 3450 1200 4275 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200 +4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001 +-6 +6 4725 1200 5925 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200 +4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001 +-6 +6 6000 2700 7200 3225 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700 +4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001 +-6 +6 6000 4950 7200 5475 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950 +4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001 +-6 +6 8700 3000 9375 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000 +4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001 +-6 +6 4350 3900 5025 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900 +4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001 +4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001 +-6 +6 4275 2550 5100 2850 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550 +4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001 +-6 +6 6000 3900 7200 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900 +4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001 +-6 +6 1800 6000 7800 8100 +6 1800 6000 7800 7200 +6 1800 6900 2700 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900 +4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001 +-6 +6 4725 6900 5625 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900 +4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001 +-6 +6 6675 6900 7800 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900 +4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001 +-6 +6 5700 6900 6600 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900 +4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001 +-6 +6 4275 6000 5100 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000 +4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 + 2250 6900 2250 6600 7200 6600 7200 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3225 6900 3225 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4200 6900 4200 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5175 6900 5175 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6150 6900 6150 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6600 4650 6300 +4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001 +4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 madwifi\001 +-6 +6 3525 7800 5775 8100 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800 +4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001 +-6 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 2250 7200 4200 7800 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 7200 7200 5100 7800 +-6 +6 9600 3000 10275 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 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 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 +4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001 +-6 +6 9300 4725 10350 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725 +4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001 +-6 +6 8175 5100 9225 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100 +4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001 +-6 +6 9300 5100 10350 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100 +4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001 +-6 +6 8175 5475 9225 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475 +4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001 +-6 +6 9300 5475 10350 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475 +4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001 +-6 +6 8175 5850 9225 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 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 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 +4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001 +-6 +6 8175 6225 9225 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225 +4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001 +-6 +6 9300 5850 10350 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 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 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 + 4500 2550 3900 1500 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4800 2550 5400 1500 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4200 4350 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 3900 6000 3000 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4200 6000 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6000 4650 4425 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 4425 6600 4950 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 3225 6600 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 5250 8100 5250 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9075 4425 9075 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3000 8700 3150 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 3900 4650 2850 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 4125 8700 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 4350 5025 6000 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 3150 4875 6000 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 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 210 1440 1637 2371 wpa_supplicant\001 diff --git a/wpa_supplicant/eap_testing.txt b/wpa_supplicant/eap_testing.txt new file mode 100644 index 0000000000000..8d132223f6a04 --- /dev/null +++ b/wpa_supplicant/eap_testing.txt @@ -0,0 +1,392 @@ +Automatic regression and interoperability testing of wpa_supplicant's +IEEE 802.1X/EAPOL authentication + +Test program: +- Linked some parts of IEEE 802.1X Authenticator implementation from + hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS + encapsulation/decapsulation) into wpa_supplicant. +- Replaced wpa_supplicant.c and wpa.c with test code that trigger + IEEE 802.1X authentication automatically without need for wireless + client card or AP. +- For EAP methods that generate keying material, the key derived by the + Supplicant is verified to match with the one received by the (now + integrated) Authenticator. + +The full automated test suite can now be run in couple of seconds, but +I'm more than willing to add new RADIUS authentication servers to make +this take a bit more time.. ;-) As an extra bonus, this can also be +seen as automatic regression/interoperability testing for the RADIUS +server, too. + +In order for me to be able to use a new authentication server, the +server need to be available from Internet (at least from one static IP +address) and I will need to get suitable user name/password pairs, +certificates, and private keys for testing use. Other alternative +would be to get an evaluation version of the server so that I can +install it on my own test setup. If you are interested in providing +either server access or evaluation version, please contact me +(j@w1.fi). + + +Test matrix + ++) tested successfully +F) failed +-) server did not support +?) not tested + +Cisco ACS ----------------------------------------------------------. +hostapd --------------------------------------------------------. | +Cisco Aironet 1200 AP (local RADIUS server) ----------------. | | +Periodik Labs Elektron ---------------------------------. | | | +Lucent NavisRadius ---------------------------------. | | | | +Interlink RAD-Series ---------------------------. | | | | | +Radiator -----------------------------------. | | | | | | +Meetinghouse Aegis ---------------------. | | | | | | | +Funk Steel-Belted ------------------. | | | | | | | | +Funk Odyssey -------------------. | | | | | | | | | +Microsoft IAS --------------. | | | | | | | | | | +FreeRADIUS -------------. | | | | | | | | | | | + | | | | | | | | | | | | + +EAP-MD5 + - - + + + + + - - + + +EAP-GTC + - - ? + + + + - - + - +EAP-OTP - - - - - + - - - - - - +EAP-MSCHAPv2 + - - + + + + + - - + - +EAP-TLS + + + + + + + + - - + + +EAP-PEAPv0/MSCHAPv2 + + + + + + + + + - + + +EAP-PEAPv0/GTC + - + - + + + + - - + + +EAP-PEAPv0/OTP - - - - - + - - - - - - +EAP-PEAPv0/MD5 + - - + + + + + - - + - +EAP-PEAPv0/TLS + + - + + + F + - - + + +EAP-PEAPv0/SIM - - - - - - - - - - + - +EAP-PEAPv0/AKA - - - - - - - - - - + - +EAP-PEAPv0/PSK - - - - - - - - - - + - +EAP-PEAPv0/PAX - - - - - - - - - - + - +EAP-PEAPv0/SAKE - - - - - - - - - - + - +EAP-PEAPv0/GPSK - - - - - - - - - - + - +EAP-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + + +EAP-PEAPv1/GTC - - + + + +1 + +5 +8 - + + +EAP-PEAPv1/OTP - - - - - +1 - - - - - - +EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + - +EAP-PEAPv1/TLS - - - + + +1 F +5 - - + + +EAP-PEAPv1/SIM - - - - - - - - - - + - +EAP-PEAPv1/AKA - - - - - - - - - - + - +EAP-PEAPv1/PSK - - - - - - - - - - + - +EAP-PEAPv1/PAX - - - - - - - - - - + - +EAP-PEAPv1/SAKE - - - - - - - - - - + - +EAP-PEAPv1/GPSK - - - - - - - - - - + - +EAP-TTLS/CHAP + - +2 + + + + + + - + - +EAP-TTLS/MSCHAP + - + + + + + + + - + - +EAP-TTLS/MSCHAPv2 + - + + + + + + + - + - +EAP-TTLS/PAP + - + + + + + + + - + - +EAP-TTLS/EAP-MD5 + - +2 + + + + + + - + - +EAP-TTLS/EAP-GTC + - +2 ? + + + + - - + - +EAP-TTLS/EAP-OTP - - - - - + - - - - - - +EAP-TTLS/EAP-MSCHAPv2 + - +2 + + + + + + - + - +EAP-TTLS/EAP-TLS + - +2 + F + + + - - + - +EAP-TTLS/EAP-SIM - - - - - - - - - - + - +EAP-TTLS/EAP-AKA - - - - - - - - - - + - +EAP-TTLS/EAP-PSK - - - - - - - - - - + - +EAP-TTLS/EAP-PAX - - - - - - - - - - + - +EAP-TTLS/EAP-SAKE - - - - - - - - - - + - +EAP-TTLS/EAP-GPSK - - - - - - - - - - + - +EAP-TTLS + TNC - - - - - + - - - - + - +EAP-SIM + - - ? - + - ? - - + - +EAP-AKA - - - - - + - - - - + - +EAP-AKA' - - - - - - - - - - + - +EAP-PSK +7 - - - - + - - - - + - +EAP-PAX - - - - - + - - - - + - +EAP-SAKE - - - - - - - - - - + - +EAP-GPSK - - - - - - - - - - + - +EAP-FAST/MSCHAPv2(prov) - - - + - + - - - + + + +EAP-FAST/GTC(auth) - - - + - + - - - + + + +EAP-FAST/MSCHAPv2(aprov)- - - - - + - - - - + + +EAP-FAST/GTC(aprov) - - - - - + - - - - + + +EAP-FAST/MD5(aprov) - - - - - + - - - - + - +EAP-FAST/TLS(aprov) - - - - - - - - - - + + +EAP-FAST/SIM(aprov) - - - - - - - - - - + - +EAP-FAST/AKA(aprov) - - - - - - - - - - + - +EAP-FAST/MSCHAPv2(auth) - - - - - + - - - - + + +EAP-FAST/MD5(auth) - - - - - + - - - - + - +EAP-FAST/TLS(auth) - - - - - - - - - - + + +EAP-FAST/SIM(auth) - - - - - - - - - - + - +EAP-FAST/AKA(auth) - - - - - - - - - - + - +EAP-FAST + TNC - - - - - - - - - - + - +LEAP + - + + + + F +6 - + - + +EAP-TNC +9 - - - - + - - - - + - +EAP-IKEv2 +10 - - - - - - - - - + - + +1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP + encryption", during key derivation (requires phase1="peaplabel=1" in the + network configuration in wpa_supplicant.conf) +2) used FreeRADIUS as inner auth server +5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new + label in key deriviation + (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode) +6) Authenticator simulator required patching for handling Access-Accept within + negotiation (for the first EAP-Success of LEAP) +7) tested only with an older (incompatible) draft of EAP-PSK; FreeRADIUS does + not support the current EAP-PSK (RFC) specification +8) PEAPv1 used non-standard version negotiation (client had to force v1 even + though server reported v0 as the highest supported version) +9) only EAP-TTLS/EAP-TNC tested, i.e., test did not include proper sequence of + client authentication followed by TNC inside the tunnel +10) worked only with special compatibility code to match the IKEv2 server + implementation + + +Automated tests: + +FreeRADIUS (2.0-beta/CVS snapshot) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +- EAP-TTLS / CHAP +- EAP-TTLS / PAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / EAP-TNC (partial support; no authentication sequence) +- EAP-SIM +- LEAP + +Microsoft Windows Server 2003 / IAS +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-MD5 +* IAS does not seem to support other EAP methods + +Funk Odyssey 2.01.00.653 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv) +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv) +* not supported in Odyssey: + - EAP-MD5-Challenge + - EAP-GTC + - EAP-MSCHAPv2 + - EAP-PEAP / MD5-Challenge + - EAP-PEAP / TLS + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- EAP-MD5-Challenge +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / MD5 +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / MD5 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + +Meetinghouse Aegis 1.1.4 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / TLS +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +* did not work + - EAP-TTLS / EAP-TLS + (Server rejects authentication without any reason in debug log. It + looks like the inner TLS negotiation starts properly and the last + packet from Supplicant looks like the one sent in the Phase 1. The + server generates a valid looking reply in the same way as in Phase + 1, but then ends up sending Access-Reject. Maybe an issue with TTLS + fragmentation in the Aegis server(?) The packet seems to include + 1328 bytes of EAP-Message and this may go beyond the fragmentation + limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS + did work, so this issue seems to be with something TTLS specific.) + +Radiator 3.17.1 (eval, with all patches up to and including 2007-05-25) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-OTP +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / OTP +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / OTP +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-OTP +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. +- EAP-SIM +- EAP-AKA +- EAP-PSK +- EAP-PAX +- EAP-TNC + +Interlink Networks RAD-Series 6.1.2.7 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +* did not work + - EAP-PEAPv0 / TLS + - EAP-PEAPv1 / TLS + (Failed to decrypt Phase 2 data) + +Lucent NavisRadius 4.4.0 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1" + 'Cisco ACU 5.05' mode works without phase1 configuration +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-TLS + +Note: user certificate from NavisRadius had private key in a format +that wpa_supplicant could not use. Converting this to PKCS#12 and then +back to PEM allowed wpa_supplicant to use the key. + + +hostapd v0.3.3 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-SIM +- EAP-PAX + +PEAPv1: + +Funk Odyssey 2.01.00.653: +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Radiator 3.9: +- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message + after this +- uses label "client PEAP encryption" + +Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode): +- sends tunneled EAP-Success with MPPE keys and expects the authentication to + terminate at this point (gets somewhat confused with reply to this) +- uses label "client PEAP encryption" +- phase1="peap_outer_success=0 peaplabel=1" + +Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode): +- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK + as a reply +- uses label "client EAP encryption" + +Meetinghouse Aegis 1.1.4 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- peap_outer_success 1 and 2 work diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c new file mode 100644 index 0000000000000..d87aad7349acd --- /dev/null +++ b/wpa_supplicant/eapol_test.c @@ -0,0 +1,1216 @@ +/* + * WPA Supplicant - test code + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include "includes.h" +#include <assert.h> + +#include "common.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eap_peer/eap.h" +#include "eloop.h" +#include "wpa.h" +#include "eap_peer/eap_i.h" +#include "wpa_supplicant_i.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + +struct wpa_driver_ops *wpa_supplicant_drivers[] = { NULL }; + + +struct extra_radius_attr { + u8 type; + char syntax; + char *data; + struct extra_radius_attr *next; +}; + +struct eapol_test_data { + struct wpa_supplicant *wpa_s; + + int eapol_test_num_reauths; + int no_mppe_keys; + int num_mppe_ok, num_mppe_mismatch; + + u8 radius_identifier; + struct radius_msg *last_recv_radius; + struct in_addr own_ip_addr; + struct radius_client_data *radius; + struct hostapd_radius_servers *radius_conf; + + u8 *last_eap_radius; /* last received EAP Response from Authentication + * Server */ + size_t last_eap_radius_len; + + u8 authenticator_pmk[PMK_LEN]; + size_t authenticator_pmk_len; + int radius_access_accept_received; + int radius_access_reject_received; + int auth_timed_out; + + u8 *eap_identity; + size_t eap_identity_len; + + char *connect_info; + u8 own_addr[ETH_ALEN]; + struct extra_radius_attr *extra_attrs; +}; + +static struct eapol_test_data eapol_test; + + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx); + + +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) +{ + if (addr) + wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "%s", txt); +} + + +static int add_extra_attr(struct radius_msg *msg, + struct extra_radius_attr *attr) +{ + size_t len; + char *pos; + u32 val; + char buf[128]; + + switch (attr->syntax) { + case 's': + os_snprintf(buf, sizeof(buf), "%s", attr->data); + len = os_strlen(buf); + break; + case 'n': + buf[0] = '\0'; + len = 1; + break; + case 'x': + pos = attr->data; + if (pos[0] == '0' && pos[1] == 'x') + pos += 2; + len = os_strlen(pos); + if ((len & 1) || (len / 2) > sizeof(buf)) { + printf("Invalid extra attribute hexstring\n"); + return -1; + } + len /= 2; + if (hexstr2bin(pos, (u8 *) buf, len) < 0) { + printf("Invalid extra attribute hexstring\n"); + return -1; + } + break; + case 'd': + val = htonl(atoi(attr->data)); + os_memcpy(buf, &val, 4); + len = 4; + break; + default: + printf("Incorrect extra attribute syntax specification\n"); + return -1; + } + + if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) { + printf("Could not add attribute %d\n", attr->type); + return -1; + } + + return 0; +} + + +static int add_extra_attrs(struct radius_msg *msg, + struct extra_radius_attr *attrs) +{ + struct extra_radius_attr *p; + for (p = attrs; p; p = p->next) { + if (add_extra_attr(msg, p) < 0) + return -1; + } + return 0; +} + + +static struct extra_radius_attr * +find_extra_attr(struct extra_radius_attr *attrs, u8 type) +{ + struct extra_radius_attr *p; + for (p = attrs; p; p = p->next) { + if (p->type == type) + return p; + } + return NULL; +} + + +static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + const struct eap_hdr *hdr; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + e->radius_identifier = radius_client_get_id(e->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + e->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e)); + + hdr = (const struct eap_hdr *) eap; + pos = (const u8 *) (hdr + 1); + if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE && + pos[0] == EAP_TYPE_IDENTITY) { + pos++; + os_free(e->eap_identity); + e->eap_identity_len = len - sizeof(*hdr) - 1; + e->eap_identity = os_malloc(e->eap_identity_len); + if (e->eap_identity) { + os_memcpy(e->eap_identity, pos, e->eap_identity_len); + wpa_hexdump(MSG_DEBUG, "Learned identity from " + "EAP-Response-Identity", + e->eap_identity, e->eap_identity_len); + } + } + + if (e->eap_identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + e->eap_identity, e->eap_identity_len)) { + printf("Could not add User-Name\n"); + goto fail; + } + + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &e->own_ip_addr, 4)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(e->wpa_s->own_addr)); + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID) + && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + printf("Could not add Framed-MTU\n"); + goto fail; + } + + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "%s", e->connect_info); + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + if (add_extra_attrs(msg, e->extra_attrs) < 0) + goto fail; + + if (eap && !radius_msg_add_eap(msg, eap, len)) { + printf("Could not add EAP-Message\n"); + goto fail; + } + + /* State attribute must be copied if and only if this packet is + * Access-Request reply to the previous Access-Challenge */ + if (e->last_recv_radius && e->last_recv_radius->hdr->code == + RADIUS_CODE_ACCESS_CHALLENGE) { + int res = radius_msg_copy_attr(msg, e->last_recv_radius, + RADIUS_ATTR_STATE); + if (res < 0) { + printf("Could not copy State attribute from previous " + "Access-Challenge\n"); + goto fail; + } + if (res > 0) { + wpa_printf(MSG_DEBUG, " Copied RADIUS State " + "Attribute"); + } + } + + radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr); + return; + + fail: + radius_msg_free(msg); + os_free(msg); +} + + +static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + /* struct wpa_supplicant *wpa_s = ctx; */ + printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n", + type, (unsigned long) len); + if (type == IEEE802_1X_TYPE_EAP_PACKET) { + wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len); + ieee802_1x_encapsulate_radius(&eapol_test, buf, len); + } + return 0; +} + + +static void eapol_test_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); +} + + +static const struct wpa_config_blob * +eapol_test_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} + + +static void eapol_test_eapol_done_cb(void *ctx) +{ + printf("WPA: EAPOL processing complete\n"); +} + + +static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_test_data *e = eloop_ctx; + printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n"); + e->radius_access_accept_received = 0; + send_eap_request_identity(e->wpa_s, NULL); +} + + +static int eapol_test_compare_pmk(struct eapol_test_data *e) +{ + u8 pmk[PMK_LEN]; + int ret = 1; + + if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN); + if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) { + printf("WARNING: PMK mismatch\n"); + wpa_hexdump(MSG_DEBUG, "PMK from AS", + e->authenticator_pmk, PMK_LEN); + } else if (e->radius_access_accept_received) + ret = 0; + } else if (e->authenticator_pmk_len == 16 && + eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) { + wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16); + if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) { + printf("WARNING: PMK mismatch\n"); + wpa_hexdump(MSG_DEBUG, "PMK from AS", + e->authenticator_pmk, 16); + } else if (e->radius_access_accept_received) + ret = 0; + } else if (e->radius_access_accept_received && e->no_mppe_keys) { + /* No keying material expected */ + ret = 0; + } + + if (ret && !e->no_mppe_keys) + e->num_mppe_mismatch++; + else if (!e->no_mppe_keys) + e->num_mppe_ok++; + + return ret; +} + + +static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +{ + struct eapol_test_data *e = ctx; + printf("eapol_sm_cb: success=%d\n", success); + e->eapol_test_num_reauths--; + if (e->eapol_test_num_reauths < 0) + eloop_terminate(); + else { + eapol_test_compare_pmk(e); + eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL); + } +} + + +static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->scard_ctx = wpa_s->scard; + ctx->cb = eapol_sm_cb; + ctx->cb_ctx = e; + ctx->eapol_send_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = eapol_test_eapol_done_cb; + ctx->eapol_send = eapol_test_eapol_send; + ctx->set_config_blob = eapol_test_set_config_blob; + ctx->get_config_blob = eapol_test_get_config_blob; +#ifdef EAP_TLS_OPENSSL + 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; +#endif /* EAP_TLS_OPENSSL */ + + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + os_free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + + wpa_s->current_ssid = ssid; + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + + return 0; +} + + +static void test_eapol_clean(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s) +{ + struct extra_radius_attr *p, *prev; + + radius_client_deinit(e->radius); + os_free(e->last_eap_radius); + if (e->last_recv_radius) { + radius_msg_free(e->last_recv_radius); + os_free(e->last_recv_radius); + } + os_free(e->eap_identity); + e->eap_identity = NULL; + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + if (e->radius_conf && e->radius_conf->auth_server) { + os_free(e->radius_conf->auth_server->shared_secret); + os_free(e->radius_conf->auth_server); + } + os_free(e->radius_conf); + e->radius_conf = NULL; + scard_deinit(wpa_s->scard); + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + wpa_config_free(wpa_s->conf); + + p = e->extra_attrs; + while (p) { + prev = p; + p = p->next; + os_free(prev); + } +} + + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + u8 buf[100], *pos; + struct ieee802_1x_hdr *hdr; + struct eap_hdr *eap; + + hdr = (struct ieee802_1x_hdr *) buf; + hdr->version = EAPOL_VERSION; + hdr->type = IEEE802_1X_TYPE_EAP_PACKET; + hdr->length = htons(5); + + eap = (struct eap_hdr *) (hdr + 1); + eap->code = EAP_CODE_REQUEST; + eap->identifier = 0; + eap->length = htons(5); + pos = (u8 *) (eap + 1); + *pos = EAP_TYPE_IDENTITY; + + printf("Sending fake EAP-Request-Identity\n"); + eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf, + sizeof(*hdr) + 5); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_test_data *e = eloop_ctx; + printf("EAPOL test timed out\n"); + e->auth_timed_out = 1; + eloop_terminate(); +} + + +static char *eap_type_text(u8 type) +{ + switch (type) { + case EAP_TYPE_IDENTITY: return "Identity"; + case EAP_TYPE_NOTIFICATION: return "Notification"; + case EAP_TYPE_NAK: return "Nak"; + case EAP_TYPE_TLS: return "TLS"; + case EAP_TYPE_TTLS: return "TTLS"; + case EAP_TYPE_PEAP: return "PEAP"; + case EAP_TYPE_SIM: return "SIM"; + case EAP_TYPE_GTC: return "GTC"; + case EAP_TYPE_MD5: return "MD5"; + case EAP_TYPE_OTP: return "OTP"; + case EAP_TYPE_FAST: return "FAST"; + case EAP_TYPE_SAKE: return "SAKE"; + case EAP_TYPE_PSK: return "PSK"; + default: return "Unknown"; + } +} + + +static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) +{ + u8 *eap; + size_t len; + struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + + if (e->last_recv_radius == NULL) + return; + + msg = e->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + wpa_printf(MSG_DEBUG, "could not extract " + "EAP-Message from RADIUS message"); + os_free(e->last_eap_radius); + e->last_eap_radius = NULL; + e->last_eap_radius_len = 0; + return; + } + + if (len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "too short EAP packet " + "received from authentication server"); + os_free(eap); + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + os_strlcpy(buf, "EAP Success", sizeof(buf)); + /* LEAP uses EAP Success within an authentication, so must not + * stop here with eloop_terminate(); */ + break; + case EAP_CODE_FAILURE: + os_strlcpy(buf, "EAP Failure", sizeof(buf)); + eloop_terminate(); + break; + default: + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); + wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + break; + } + wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, ntohs(hdr->length), buf); + + /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ + + os_free(e->last_eap_radius); + e->last_eap_radius = eap; + e->last_eap_radius_len = len; + + { + struct ieee802_1x_hdr *dot1x; + dot1x = os_malloc(sizeof(*dot1x) + len); + assert(dot1x != NULL); + dot1x->version = EAPOL_VERSION; + dot1x->type = IEEE802_1X_TYPE_EAP_PACKET; + dot1x->length = htons(len); + os_memcpy((u8 *) (dot1x + 1), eap, len); + eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, + (u8 *) dot1x, sizeof(*dot1x) + len); + os_free(dot1x); + } +} + + +static void ieee802_1x_get_keys(struct eapol_test_data *e, + struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + if (keys && keys->send == NULL && keys->recv == NULL) { + os_free(keys); + keys = radius_msg_get_cisco_keys(msg, req, shared_secret, + shared_secret_len); + } + + if (keys) { + if (keys->send) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)", + keys->send, keys->send_len); + } + if (keys->recv) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)", + keys->recv, keys->recv_len); + e->authenticator_pmk_len = + keys->recv_len > PMK_LEN ? PMK_LEN : + keys->recv_len; + os_memcpy(e->authenticator_pmk, keys->recv, + e->authenticator_pmk_len); + if (e->authenticator_pmk_len == 16 && keys->send && + keys->send_len == 16) { + /* MS-CHAP-v2 derives 16 octet keys */ + wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key " + "to extend PMK to 32 octets"); + os_memcpy(e->authenticator_pmk + + e->authenticator_pmk_len, + keys->send, keys->send_len); + e->authenticator_pmk_len += keys->send_len; + } + } + + os_free(keys->send); + os_free(keys->recv); + os_free(keys); + } +} + + +/* Process the RADIUS frames from Authentication Server */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + 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 && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS " + "Access-Reject without Message-Authenticator " + "since it does not include EAP-Message\n"); + } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, + req, 1)) { + printf("Incoming RADIUS packet did not have correct " + "Message-Authenticator - dropped\n"); + return RADIUS_RX_UNKNOWN; + } + + if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && + msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + e->radius_identifier = -1; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); + + if (e->last_recv_radius) { + radius_msg_free(e->last_recv_radius); + os_free(e->last_recv_radius); + } + + e->last_recv_radius = msg; + + switch (msg->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + e->radius_access_accept_received = 1; + ieee802_1x_get_keys(e, msg, req, shared_secret, + shared_secret_len); + break; + case RADIUS_CODE_ACCESS_REJECT: + e->radius_access_reject_received = 1; + break; + } + + ieee802_1x_decapsulate_radius(e); + + if ((msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + e->eapol_test_num_reauths < 0) || + msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) { + eloop_terminate(); + } + + return RADIUS_RX_QUEUED; +} + + +static void wpa_init_conf(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s, const char *authsrv, + int port, const char *secret, + const char *cli_addr) +{ + struct hostapd_radius_server *as; + int res; + + wpa_s->bssid[5] = 1; + os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN); + e->own_ip_addr.s_addr = htonl((127 << 24) | 1); + os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); + + e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers)); + assert(e->radius_conf != NULL); + e->radius_conf->num_auth_servers = 1; + as = os_zalloc(sizeof(struct hostapd_radius_server)); + assert(as != NULL); +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &as->addr.u.v4; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(authsrv, &as->addr.u.v4); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + as->addr.af = AF_INET; + as->port = port; + as->shared_secret = (u8 *) os_strdup(secret); + as->shared_secret_len = os_strlen(secret); + e->radius_conf->auth_server = as; + e->radius_conf->auth_servers = as; + e->radius_conf->msg_dumps = 1; + if (cli_addr) { + if (hostapd_parse_ip_addr(cli_addr, + &e->radius_conf->client_addr) == 0) + e->radius_conf->force_client_addr = 1; + else { + wpa_printf(MSG_ERROR, "Invalid IP address '%s'", + cli_addr); + assert(0); + } + } + + e->radius = radius_client_init(wpa_s, e->radius_conf); + assert(e->radius != NULL); + + res = radius_client_register(e->radius, RADIUS_AUTH, + ieee802_1x_receive_auth, e); + assert(res == 0); +} + + +static int scard_test(void) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char _rand[16]; +#ifdef PCSC_FUNCS + unsigned char sres[4]; + unsigned char kc[8]; +#endif /* PCSC_FUNCS */ +#define num_triplets 5 + unsigned char rand_[num_triplets][16]; + unsigned char sres_[num_triplets][4]; + unsigned char kc_[num_triplets][8]; + int i, res; + size_t j; + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + unsigned char aka_rand[AKA_RAND_LEN]; + unsigned char aka_autn[AKA_AUTN_LEN]; + unsigned char aka_auts[AKA_AUTS_LEN]; + unsigned char aka_res[RES_MAX_LEN]; + size_t aka_res_len; + unsigned char aka_ik[IK_LEN]; + unsigned char aka_ck[CK_LEN]; + + scard = scard_init(SCARD_TRY_BOTH); + if (scard == NULL) + return -1; + if (scard_set_pin(scard, "1234")) { + wpa_printf(MSG_WARNING, "PIN validation failed"); + scard_deinit(scard); + return -1; + } + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) + goto failed; + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len); + /* NOTE: Permanent Username: 1 | IMSI */ + + os_memset(_rand, 0, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + goto failed; + + os_memset(_rand, 0xff, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + goto failed; + + for (i = 0; i < num_triplets; i++) { + os_memset(rand_[i], i, sizeof(rand_[i])); + if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i])) + goto failed; + } + + for (i = 0; i < num_triplets; i++) { + printf("1"); + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(","); + for (j = 0; j < 16; j++) + printf("%02X", rand_[i][j]); + printf(","); + for (j = 0; j < 4; j++) + printf("%02X", sres_[i][j]); + printf(","); + for (j = 0; j < 8; j++) + printf("%02X", kc_[i][j]); + printf("\n"); + } + + wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication"); + + /* seq 39 (0x28) */ + os_memset(aka_rand, 0xaa, 16); + os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf" + "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16); + + res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len, + aka_ik, aka_ck, aka_auts); + if (res == 0) { + wpa_printf(MSG_DEBUG, "UMTS auth completed successfully"); + wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len); + wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN); + wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN); + } else if (res == -2) { + wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization " + "failure"); + wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN); + } else { + wpa_printf(MSG_DEBUG, "UMTS auth failed"); + } + +failed: + scard_deinit(scard); + + return 0; +#undef num_triplets +} + + +static int scard_get_triplets(int argc, char *argv[]) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char _rand[16]; + unsigned char sres[4]; + unsigned char kc[8]; + int num_triplets; + int i; + size_t j; + + if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) { + printf("invalid parameters for sim command\n"); + return -1; + } + + if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) { + /* disable debug output */ + wpa_debug_level = 99; + } + + scard = scard_init(SCARD_GSM_SIM_ONLY); + if (scard == NULL) { + printf("Failed to open smartcard connection\n"); + return -1; + } + if (scard_set_pin(scard, argv[0])) { + wpa_printf(MSG_WARNING, "PIN validation failed"); + scard_deinit(scard); + return -1; + } + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) { + scard_deinit(scard); + return -1; + } + + for (i = 0; i < num_triplets; i++) { + os_memset(_rand, i, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + break; + + /* IMSI:Kc:SRES:RAND */ + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(":"); + for (j = 0; j < 8; j++) + printf("%02X", kc[j]); + printf(":"); + for (j = 0; j < 4; j++) + printf("%02X", sres[j]); + printf(":"); + for (j = 0; j < 16; j++) + printf("%02X", _rand[j]); + printf("\n"); + } + + scard_deinit(scard); + + return 0; +} + + +static void eapol_test_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +static void usage(void) +{ + printf("usage:\n" + "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] " + "[-s<AS secret>]\\\n" + " [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n" + " [-M<client MAC address>] \\\n" + " [-N<attr spec>] \\\n" + " [-A<client IP>]\n" + "eapol_test scard\n" + "eapol_test sim <PIN> <num triplets> [debug]\n" + "\n"); + printf("options:\n" + " -c<conf> = configuration file\n" + " -a<AS IP> = IP address of the authentication server, " + "default 127.0.0.1\n" + " -p<AS port> = UDP port of the authentication server, " + "default 1812\n" + " -s<AS secret> = shared secret with the authentication " + "server, default 'radius'\n" + " -A<client IP> = IP address of the client, default: select " + "automatically\n" + " -r<count> = number of re-authentications\n" + " -W = wait for a control interface monitor before starting\n" + " -S = save configuration after authentication\n" + " -n = no MPPE keys expected\n" + " -t<timeout> = sets timeout in seconds (default: 30 s)\n" + " -C<Connect-Info> = RADIUS Connect-Info (default: " + "CONNECT 11Mbps 802.11b)\n" + " -M<client MAC address> = Set own MAC address " + "(Calling-Station-Id,\n" + " default: 02:00:00:00:00:01)\n" + " -N<attr spec> = send arbitrary attribute specified by:\n" + " attr_id:syntax:value or attr_id\n" + " attr_id - number id of the attribute\n" + " syntax - one of: s, d, x\n" + " s = string\n" + " d = integer\n" + " x = octet string\n" + " value - attribute value.\n" + " When only attr_id is specified, NULL will be used as " + "value.\n" + " Multiple attributes can be specified by using the " + "option several times.\n"); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int c, ret = 1, wait_for_monitor = 0, save_config = 0; + char *as_addr = "127.0.0.1"; + int as_port = 1812; + char *as_secret = "radius"; + char *cli_addr = NULL; + char *conf = NULL; + int timeout = 30; + char *pos; + struct extra_radius_attr *p = NULL, *p1; + + if (os_program_init()) + return -1; + + hostapd_logger_register_cb(hostapd_logger_cb); + + os_memset(&eapol_test, 0, sizeof(eapol_test)); + eapol_test.connect_info = "CONNECT 11Mbps 802.11b"; + os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + for (;;) { + c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W"); + if (c < 0) + break; + switch (c) { + case 'a': + as_addr = optarg; + break; + case 'A': + cli_addr = optarg; + break; + case 'c': + conf = optarg; + break; + case 'C': + eapol_test.connect_info = optarg; + break; + case 'M': + if (hwaddr_aton(optarg, eapol_test.own_addr)) { + usage(); + return -1; + } + break; + case 'n': + eapol_test.no_mppe_keys++; + break; + case 'p': + as_port = atoi(optarg); + break; + case 'r': + eapol_test.eapol_test_num_reauths = atoi(optarg); + break; + case 's': + as_secret = optarg; + break; + case 'S': + save_config++; + break; + case 't': + timeout = atoi(optarg); + break; + case 'W': + wait_for_monitor++; + break; + case 'N': + p1 = os_zalloc(sizeof(p1)); + if (p1 == NULL) + break; + if (!p) + eapol_test.extra_attrs = p1; + else + p->next = p1; + p = p1; + + p->type = atoi(optarg); + pos = os_strchr(optarg, ':'); + if (pos == NULL) { + p->syntax = 'n'; + p->data = NULL; + break; + } + + pos++; + if (pos[0] == '\0' || pos[1] != ':') { + printf("Incorrect format of attribute " + "specification\n"); + break; + } + + p->syntax = pos[0]; + p->data = pos + 2; + break; + default: + usage(); + return -1; + } + } + + if (argc > optind && os_strcmp(argv[optind], "scard") == 0) { + return scard_test(); + } + + if (argc > optind && os_strcmp(argv[optind], "sim") == 0) { + return scard_get_triplets(argc - optind - 1, + &argv[optind + 1]); + } + + if (conf == NULL) { + usage(); + printf("Configuration file is required.\n"); + return -1; + } + + if (eap_peer_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + if (eloop_init(&wpa_s)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + eapol_test.wpa_s = &wpa_s; + wpa_s.conf = wpa_config_read(conf); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", conf); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret, + cli_addr); + wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); + if (wpa_s.ctrl_iface == NULL) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another eapol_test process already " + "running or the file was\n" + "left by an unclean termination of eapol_test in " + "which case you will need\n" + "to manually remove this file before starting " + "eapol_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; + + if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) + return -1; + + if (wait_for_monitor) + wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); + + eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test, + NULL); + eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); + eloop_register_signal_terminate(eapol_test_terminate, NULL); + eloop_register_signal_reconfig(eapol_test_terminate, NULL); + eloop_run(); + + eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL); + eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL); + + if (eapol_test_compare_pmk(&eapol_test) == 0 || + eapol_test.no_mppe_keys) + ret = 0; + if (eapol_test.auth_timed_out) + ret = -2; + if (eapol_test.radius_access_reject_received) + ret = -3; + + if (save_config) + wpa_config_write(conf, wpa_s.conf); + + test_eapol_clean(&eapol_test, &wpa_s); + + eap_peer_unregister_methods(); + + eloop_destroy(); + + printf("MPPE keys OK: %d mismatch: %d\n", + eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch); + if (eapol_test.num_mppe_mismatch) + ret = -4; + if (ret) + printf("FAILURE\n"); + else + printf("SUCCESS\n"); + + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c new file mode 100644 index 0000000000000..dd4595a911f23 --- /dev/null +++ b/wpa_supplicant/events.c @@ -0,0 +1,1116 @@ +/* + * WPA Supplicant - Driver event processing + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "eloop.h" +#include "drivers/driver.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "wpa_supplicant_i.h" +#include "pcsc_funcs.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_ctrl.h" +#include "eap_peer/eap.h" +#include "ctrl_iface_dbus.h" +#include "ieee802_11_defs.h" +#include "blacklist.h" +#include "wpas_glue.h" +#include "wps_supplicant.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; + + wpa_printf(MSG_DEBUG, "Select network based on association " + "information"); + 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 | + WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)) { + u8 wpa_ie[80]; + size_t wpa_ie_len = sizeof(wpa_ie); + wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len); + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + } + + if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) + eapol_sm_invalidate_cached_session(wpa_s->eapol); + wpa_s->current_ssid = ssid; + wpa_supplicant_rsn_supp_set_config(wpa_s, 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); + } +} + + +void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + wpa_s->ap_ies_from_associnfo = 0; +} + + +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ie_data ie; + int pmksa_set = -1; + size_t i; + + if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 || + ie.pmkid == NULL) + return; + + for (i = 0; i < ie.num_pmkid; i++) { + pmksa_set = pmksa_cache_set_current(wpa_s->wpa, + ie.pmkid + i * PMKID_LEN, + NULL, NULL, 0); + if (pmksa_set == 0) { + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + break; + } + } + + wpa_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; + +#ifdef IEEE8021X_EAPOL + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && + wpa_s->current_ssid && + !(wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either + * plaintext or static WEP keys). */ + return 0; + } +#endif /* IEEE8021X_EAPOL */ + + return 1; +} + + +/** + * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC + * @wpa_s: pointer to wpa_supplicant data + * @ssid: Configuration data for the network + * Returns: 0 on success, -1 on failure + * + * This function is called when starting authentication with a network that is + * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA). + */ +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +#ifdef IEEE8021X_EAPOL + int aka = 0, sim = 0, type; + + if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) + return 0; + + if (ssid->eap.eap_methods == NULL) { + sim = 1; + aka = 1; + } else { + struct eap_method_type *eap = ssid->eap.eap_methods; + while (eap->vendor != EAP_VENDOR_IETF || + eap->method != EAP_TYPE_NONE) { + if (eap->vendor == EAP_VENDOR_IETF) { + if (eap->method == EAP_TYPE_SIM) + sim = 1; + else if (eap->method == EAP_TYPE_AKA) + aka = 1; + } + eap++; + } + } + + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) + sim = 0; + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL) + aka = 0; + + if (!sim && !aka) { + wpa_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); +#endif /* IEEE8021X_EAPOL */ + + return 0; +} + + +#ifndef CONFIG_NO_SCAN_PROCESSING +static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, + struct wpa_ssid *ssid) +{ + int i, privacy = 0; + + if (ssid->mixed_cell) + return 1; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + privacy = 1; + break; + } + } +#ifdef IEEE8021X_EAPOL + if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) + privacy = 1; +#endif /* IEEE8021X_EAPOL */ + + if (bss->caps & IEEE80211_CAP_PRIVACY) + return privacy; + return !privacy; +} + + +static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + struct wpa_ie_data ie; + int proto_match = 0; + const u8 *rsn_ie, *wpa_ie; + int ret; + + ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); + if (ret >= 0) + return ret; + + rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { + proto_match++; + + if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { + wpa_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; + } + +#ifdef CONFIG_IEEE80211W + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + ssid->ieee80211w == IEEE80211W_REQUIRED) { + wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame " + "protection"); + break; + } +#endif /* CONFIG_IEEE80211W */ + + wpa_printf(MSG_DEBUG, " selected based on RSN IE"); + return 1; + } + + wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { + proto_match++; + + if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { + wpa_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_res * +wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid) +{ + struct wpa_ssid *ssid; + struct wpa_scan_res *bss; + size_t i; + struct wpa_blacklist *e; + const u8 *ie; + + wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP"); + for (i = 0; i < wpa_s->scan_res->num; i++) { + const u8 *ssid_; + u8 wpa_ie_len, rsn_ie_len, ssid_len; + bss = wpa_s->scan_res->res[i]; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; + + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", + (int) i, MAC2STR(bss->bssid), + wpa_ssid_txt(ssid_, ssid_len), + wpa_ie_len, rsn_ie_len, bss->caps); + + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e && e->count > 1) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + if (wpa_ie_len == 0 && rsn_ie_len == 0) { + wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); + continue; + } + + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = 1; + + if (ssid->disabled) { + wpa_printf(MSG_DEBUG, " skip - disabled"); + continue; + } + +#ifdef CONFIG_WPS + if (ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; +#endif /* CONFIG_WPS */ + + if (check_ssid && + (ssid_len != ssid->ssid_len || + os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + wpa_printf(MSG_DEBUG, " skip - " + "SSID mismatch"); + continue; + } + + if (ssid->bssid_set && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, " skip - " + "BSSID mismatch"); + continue; + } + + if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) + continue; + + wpa_printf(MSG_DEBUG, " selected WPA AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(ssid_, ssid_len)); + *selected_ssid = ssid; + return bss; + } + } + + return NULL; +} + + +static struct wpa_scan_res * +wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid) +{ + struct wpa_ssid *ssid; + struct wpa_scan_res *bss; + size_t i; + struct wpa_blacklist *e; + const u8 *ie; + + wpa_printf(MSG_DEBUG, "Try to find non-WPA AP"); + for (i = 0; i < wpa_s->scan_res->num; i++) { + const u8 *ssid_; + u8 wpa_ie_len, rsn_ie_len, ssid_len; + bss = wpa_s->scan_res->res[i]; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; + + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", + (int) i, MAC2STR(bss->bssid), + wpa_ssid_txt(ssid_, ssid_len), + wpa_ie_len, rsn_ie_len, bss->caps); + + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e && e->count > 1) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = ssid->ssid_len != 0; + + if (ssid->disabled) { + wpa_printf(MSG_DEBUG, " skip - disabled"); + continue; + } + +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + /* Only allow wildcard SSID match if an AP + * advertises active WPS operation that matches + * with our mode. */ + check_ssid = 1; + if (ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, + bss)) + check_ssid = 0; + } +#endif /* CONFIG_WPS */ + + if (check_ssid && + (ssid_len != ssid->ssid_len || + os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + wpa_printf(MSG_DEBUG, " skip - " + "SSID mismatch"); + continue; + } + + if (ssid->bssid_set && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, " skip - " + "BSSID mismatch"); + continue; + } + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) + { + wpa_printf(MSG_DEBUG, " skip - " + "non-WPA network not allowed"); + continue; + } + + if ((ssid->key_mgmt & + (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256)) && + (wpa_ie_len != 0 || rsn_ie_len != 0)) { + wpa_printf(MSG_DEBUG, " skip - " + "WPA network"); + continue; + } + + if (!wpa_supplicant_match_privacy(bss, ssid)) { + wpa_printf(MSG_DEBUG, " skip - " + "privacy mismatch"); + continue; + } + + if (bss->caps & IEEE80211_CAP_IBSS) { + wpa_printf(MSG_DEBUG, " skip - " + "IBSS (adhoc) network"); + continue; + } + + wpa_printf(MSG_DEBUG, " selected non-WPA AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(ssid_, ssid_len)); + *selected_ssid = ssid; + return bss; + } + } + + return NULL; +} + + +static struct wpa_scan_res * +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, + struct wpa_ssid **selected_ssid) +{ + struct wpa_scan_res *selected; + + wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); + + /* First, try to find WPA-enabled AP */ + selected = wpa_supplicant_select_bss_wpa(wpa_s, group, selected_ssid); + if (selected) + return selected; + + /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration + * allows this. */ + return wpa_supplicant_select_bss_non_wpa(wpa_s, group, selected_ssid); +} + + +static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) +{ + int prio, timeout; + struct wpa_scan_res *selected = NULL; + struct wpa_ssid *ssid = NULL; + + 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"); + timeout = 1; + goto req_scan; + } + + /* + * Don't post the results if this was the initial cached + * and there were no results. + */ + if (wpa_s->scan_res_tried == 1 && wpa_s->conf->ap_scan == 1 && + wpa_s->scan_res->num == 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Cached scan results are " + "empty - not posting"); + } else { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + wpa_supplicant_dbus_notify_scan_results(wpa_s); + wpas_wps_notify_scan_results(wpa_s); + } + + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)) || + wpa_s->disconnected) + return; + + 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], &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); + wpa_s->blacklist_cleared++; + } else if (selected == NULL) { + break; + } + } + + if (selected) { + if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP + "PBC session overlap"); + timeout = 10; + goto req_scan; + } + + /* Do not trigger new association unless the BSSID has changed + * or if reassociation is requested. If we are in process of + * associating with the selected BSSID, do not trigger new + * attempt. */ + if (wpa_s->reassociate || + (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + (wpa_s->wpa_state != WPA_ASSOCIATING || + os_memcmp(selected->bssid, wpa_s->pending_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, wpa_s->scan_res); + } else { + wpa_printf(MSG_DEBUG, "No suitable AP found."); + timeout = 5; + goto req_scan; + } + + return; + +req_scan: + if (wpa_s->scan_res_tried == 1 && wpa_s->conf->ap_scan == 1) { + /* + * Quick recovery if the initial scan results were not + * complete when fetched before the first scan request. + */ + wpa_s->scan_res_tried++; + timeout = 0; + } + wpa_supplicant_req_scan(wpa_s, timeout, 0); +} +#endif /* CONFIG_NO_SCAN_PROCESSING */ + + +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] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_RSN && p[1] >= 2)) { + if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) + break; + found = 1; + wpa_find_assoc_pmkid(wpa_s); + break; + } + l -= len; + p += len; + } + if (!found && data->assoc_info.req_ies) + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + + /* WPA/RSN IE from Beacon/ProbeResp */ + p = data->assoc_info.beacon_ies; + l = data->assoc_info.beacon_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. + */ + wpa_found = rsn_found = 0; + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", + p, l); + break; + } + if (!wpa_found && + p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { + wpa_found = 1; + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len); + } + + if (!rsn_found && + p[0] == WLAN_EID_RSN && p[1] >= 2) { + rsn_found = 1; + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len); + } + + l -= len; + p += len; + } + + if (!wpa_found && data->assoc_info.beacon_ies) + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); + if (!rsn_found && data->assoc_info.beacon_ies) + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); + if (wpa_found || rsn_found) + wpa_s->ap_ies_from_associnfo = 1; +} + + +static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + u8 bssid[ETH_ALEN]; + int ft_completed = wpa_ft_is_completed(wpa_s->wpa); + + if (data) + wpa_supplicant_event_associnfo(wpa_s, data); + + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); + if (wpa_s->use_client_mlme) + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + if (wpa_s->use_client_mlme || + (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) { + wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" + MACSTR, MAC2STR(bssid)); + os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { + wpa_clear_keys(wpa_s, bssid); + } + if (wpa_supplicant_select_config(wpa_s) < 0) { + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + } + + 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. + */ + if (!ft_completed) { + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + } + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + wpa_s->eapol_received = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } else if (!ft_completed) { + /* Timeout for receiving the first EAPOL packet */ + wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); + } + wpa_supplicant_cancel_scan(wpa_s); + + if (wpa_s->driver_4way_handshake && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + /* + * We are done; the driver will take care of RSN 4-way + * handshake. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } +} + + +static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) +{ + const u8 *bssid; + + 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_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " + "pre-shared key may be incorrect"); + } + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpa_blacklist_add(wpa_s, bssid); + wpa_sm_notify_disassoc(wpa_s->wpa); + 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); + } + wpa_supplicant_mark_disassoc(wpa_s); +} + + +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->pending_mic_error_report) + return; + + wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report"); + wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); + wpa_s->pending_mic_error_report = 0; +} +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + + +static void +wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int pairwise; + struct os_time t; + + wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); + pairwise = (data && data->michael_mic_failure.unicast); + os_get_time(&t); + if ((wpa_s->last_michael_mic_error && + t.sec - wpa_s->last_michael_mic_error <= 60) || + wpa_s->pending_mic_error_report) { + if (wpa_s->pending_mic_error_report) { + /* + * Send the pending MIC error report immediately since + * we are going to start countermeasures and AP better + * do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, + wpa_s->pending_mic_error_pairwise); + } + + /* Send the new MIC error report immediately since we are going + * to start countermeasures and AP better do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + + /* initialize countermeasures */ + wpa_s->countermeasures = 1; + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); + + /* + * Need to wait for completion of request frame. We do not get + * any callback for the message completion, so just wait a + * short while and hope for the best. */ + os_sleep(0, 10000); + + wpa_drv_set_countermeasures(wpa_s, 1); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_MICHAEL_MIC_FAILURE); + eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + eloop_register_timeout(60, 0, + wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + /* TODO: mark the AP rejected for 60 second. STA is + * allowed to associate with another AP.. */ + } else { +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT + if (wpa_s->mic_errors_seen) { + /* + * Reduce the effectiveness of Michael MIC error + * reports as a means for attacking against TKIP if + * more than one MIC failure is noticed with the same + * PTK. We delay the transmission of the reports by a + * random time between 0 and 60 seconds in order to + * force the attacker wait 60 seconds before getting + * the information on whether a frame resulted in a MIC + * failure. + */ + u8 rval[4]; + int sec; + + if (os_get_random(rval, sizeof(rval)) < 0) + sec = os_random() % 60; + else + sec = WPA_GET_BE32(rval) % 60; + wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d " + "seconds", sec); + wpa_s->pending_mic_error_report = 1; + wpa_s->pending_mic_error_pairwise = pairwise; + eloop_cancel_timeout( + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + eloop_register_timeout( + sec, os_random() % 1000000, + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + } else { + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + } +#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + } + wpa_s->last_michael_mic_error = t.sec; + wpa_s->mic_errors_seen++; +} + + +static void +wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) + return; + + switch (data->interface_status.ievent) { + case EVENT_INTERFACE_ADDED: + if (!wpa_s->interface_removed) + break; + wpa_s->interface_removed = 0; + wpa_printf(MSG_DEBUG, "Configured interface was added."); + if (wpa_supplicant_driver_init(wpa_s) < 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; + } +} + + +#ifdef CONFIG_PEERKEY +static void +wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer); +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_IEEE80211R +static void +wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + + if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, + data->ft_ies.ies_len, + data->ft_ies.ft_action, + data->ft_ies.target_ap) < 0) { + /* TODO: prevent MLME/driver from trying to associate? */ + } +} +#endif /* CONFIG_IEEE80211R */ + + +void wpa_supplicant_event(void *ctx, wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_supplicant *wpa_s = ctx; + + 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; +#ifndef CONFIG_NO_SCAN_PROCESSING + case EVENT_SCAN_RESULTS: + wpa_supplicant_event_scan_results(wpa_s); + break; +#endif /* CONFIG_NO_SCAN_PROCESSING */ + case EVENT_ASSOCINFO: + wpa_supplicant_event_associnfo(wpa_s, data); + break; + case EVENT_INTERFACE_STATUS: + wpa_supplicant_event_interface_status(wpa_s, data); + break; + case EVENT_PMKID_CANDIDATE: + wpa_supplicant_event_pmkid_candidate(wpa_s, data); + break; +#ifdef CONFIG_PEERKEY + case EVENT_STKSTART: + wpa_supplicant_event_stkstart(wpa_s, data); + break; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211R + case EVENT_FT_RESPONSE: + wpa_supplicant_event_ft_response(wpa_s, data); + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_INFO, "Unknown event %d", event); + break; + } +} diff --git a/wpa_supplicant/examples/ieee8021x.conf b/wpa_supplicant/examples/ieee8021x.conf new file mode 100644 index 0000000000000..e8a5503d8359d --- /dev/null +++ b/wpa_supplicant/examples/ieee8021x.conf @@ -0,0 +1,13 @@ +# IEEE 802.1X with dynamic WEP keys using EAP-PEAP/MSCHAPv2 + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example 802.1x network" + key_mgmt=IEEE8021X + eap=PEAP + phase2="auth=MSCHAPV2" + identity="user name" + password="password" + ca_cert="/etc/cert/ca.pem" +} diff --git a/wpa_supplicant/examples/openCryptoki.conf b/wpa_supplicant/examples/openCryptoki.conf new file mode 100644 index 0000000000000..e2301a61cabf1 --- /dev/null +++ b/wpa_supplicant/examples/openCryptoki.conf @@ -0,0 +1,41 @@ +# EAP-TLS using private key and certificates via OpenSSL PKCS#11 engine and +# openCryptoki (e.g., with TPM token) + +# This example uses following PKCS#11 objects: +# $ pkcs11-tool --module /usr/lib/opencryptoki/libopencryptoki.so -O -l +# Please enter User PIN: +# Private Key Object; RSA +# label: rsakey +# ID: 04 +# Usage: decrypt, sign, unwrap +# Certificate Object, type = X.509 cert +# label: ca +# ID: 01 +# Certificate Object, type = X.509 cert +# label: cert +# ID: 04 + +# Configure OpenSSL to load the PKCS#11 engine and openCryptoki module +pkcs11_engine_path=/usr/lib/engines/engine_pkcs11.so +pkcs11_module_path=/usr/lib/opencryptoki/libopencryptoki.so + +network={ + ssid="test network" + key_mgmt=WPA-EAP + eap=TLS + identity="User" + + # use OpenSSL PKCS#11 engine for this network + engine=1 + engine_id="pkcs11" + + # select the private key and certificates based on ID (see pkcs11-tool + # output above) + key_id="4" + cert_id="4" + ca_cert_id="1" + + # set the PIN code; leave this out to configure the PIN to be requested + # interactively when needed (e.g., via wpa_gui or wpa_cli) + pin="123456" +} diff --git a/wpa_supplicant/examples/plaintext.conf b/wpa_supplicant/examples/plaintext.conf new file mode 100644 index 0000000000000..542ac1dd3b96a --- /dev/null +++ b/wpa_supplicant/examples/plaintext.conf @@ -0,0 +1,8 @@ +# Plaintext (no encryption) network + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example open network" + key_mgmt=NONE +} diff --git a/wpa_supplicant/examples/wep.conf b/wpa_supplicant/examples/wep.conf new file mode 100644 index 0000000000000..9c7b55f2722a2 --- /dev/null +++ b/wpa_supplicant/examples/wep.conf @@ -0,0 +1,11 @@ +# Static WEP keys + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wep network" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_tx_keyidx=0 +} diff --git a/wpa_supplicant/examples/wpa-psk-tkip.conf b/wpa_supplicant/examples/wpa-psk-tkip.conf new file mode 100644 index 0000000000000..93d7fc2444ea4 --- /dev/null +++ b/wpa_supplicant/examples/wpa-psk-tkip.conf @@ -0,0 +1,12 @@ +# WPA-PSK/TKIP + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wpa-psk network" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + group=TKIP + psk="secret passphrase" +} diff --git a/wpa_supplicant/examples/wpa2-eap-ccmp.conf b/wpa_supplicant/examples/wpa2-eap-ccmp.conf new file mode 100644 index 0000000000000..d7a64d87b2547 --- /dev/null +++ b/wpa_supplicant/examples/wpa2-eap-ccmp.conf @@ -0,0 +1,15 @@ +# WPA2-EAP/CCMP using EAP-TLS + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wpa2-eap network" + key_mgmt=WPA-EAP + proto=WPA2 + pairwise=CCMP + group=CCMP + eap=TLS + ca_cert="/etc/cert/ca.pem" + private_key="/etc/cert/user.p12" + private_key_passwd="PKCS#12 passhrase" +} diff --git a/wpa_supplicant/examples/wpas-test.py b/wpa_supplicant/examples/wpas-test.py new file mode 100755 index 0000000000000..fd7f73d428b85 --- /dev/null +++ b/wpa_supplicant/examples/wpas-test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time + +WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant" +WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant" +WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant" + +WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface" +WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces" +WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID" + +def byte_array_to_string(s): + import urllib + r = "" + for c in s: + if c >= 32 and c < 127: + r += "%c" % c + else: + r += urllib.quote(chr(c)) + return r + +def main(): + if len(sys.argv) != 2: + print "Usage: wpas-test.py <interface>" + os._exit(1) + + ifname = sys.argv[1] + + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + + # See if wpa_supplicant already knows about this interface + path = None + try: + path = wpas.getInterface(ifname) + except dbus.dbus_bindings.DBusException, exc: + if str(exc) != "wpa_supplicant knows nothing about this interface.": + raise exc + try: + path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')}) + except dbus.dbus_bindings.DBusException, exc: + if str(exc) != "wpa_supplicant already controls this interface.": + raise exc + + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + iface.scan() + # Should really wait for the "scanResults" signal instead of sleeping + time.sleep(5) + res = iface.scanResults() + + print "Scanned wireless networks:" + for opath in res: + net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath) + net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE) + props = net.properties() + + # Convert the byte-array for SSID and BSSID to printable strings + bssid = "" + for item in props["bssid"]: + bssid = bssid + ":%02x" % item + bssid = bssid[1:] + ssid = byte_array_to_string(props["ssid"]) + wpa = "no" + if props.has_key("wpaie"): + wpa = "yes" + wpa2 = "no" + if props.has_key("rsnie"): + wpa2 = "yes" + freq = 0 + if props.has_key("frequency"): + freq = props["frequency"] + caps = props["capabilities"] + qual = props["quality"] + level = props["level"] + noise = props["noise"] + maxrate = props["maxrate"] / 1000000 + + print " %s :: ssid='%s' wpa=%s wpa2=%s quality=%d%% rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq) + + wpas.removeInterface(dbus.ObjectPath(path)) + # Should fail here with unknown interface error + iface.scan() + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c new file mode 100644 index 0000000000000..6f90cc509b367 --- /dev/null +++ b/wpa_supplicant/main.c @@ -0,0 +1,264 @@ +/* + * WPA Supplicant / main() function for UNIX like OSes and MinGW + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ + +#include "common.h" +#include "wpa_supplicant_i.h" + + +static void usage(void) +{ + int i; + printf("%s\n\n%s\n" + "usage:\n" + " wpa_supplicant [-BddhKLqqtuvW] [-P<pid file>] " + "[-g<global ctrl>] \\\n" + " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] " + "[-p<driver_param>] \\\n" + " [-b<br_ifname>] [-f<debug file>] \\\n" + " [-N -i<ifname> -c<conf> [-C<ctrl>] " + "[-D<driver>] \\\n" + " [-p<driver_param>] [-b<br_ifname>] ...]\n" + "\n" + "drivers:\n", + wpa_supplicant_version, wpa_supplicant_license); + + for (i = 0; wpa_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 = optional bridge interface name\n" + " -B = run daemon in the background\n" + " -c = Configuration file\n" + " -C = ctrl_interface parameter (only used if -c is not)\n" + " -i = interface name\n" + " -d = increase debugging verbosity (-dd even more)\n" + " -D = driver name\n" +#ifdef CONFIG_DEBUG_FILE + " -f = log output to debug file instead of stdout\n" +#endif /* CONFIG_DEBUG_FILE */ + " -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"); + printf(" -p = driver parameters\n" + " -P = PID file\n" + " -q = decrease debugging verbosity (-qq even less)\n" +#ifdef CONFIG_CTRL_IFACE_DBUS + " -u = enable DBus control interface\n" +#endif /* CONFIG_CTRL_IFACE_DBUS */ + " -v = show version\n" + " -W = wait for a control interface monitor before starting\n" + " -N = start describing new interface\n"); + + printf("example:\n" + " wpa_supplicant -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%s%s%s%s\n", + wpa_supplicant_version, + wpa_supplicant_full_license1, + wpa_supplicant_full_license2, + wpa_supplicant_full_license3, + wpa_supplicant_full_license4, + wpa_supplicant_full_license5); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void wpa_supplicant_fd_workaround(void) +{ +#ifdef __linux__ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +#endif /* __linux__ */ +} + + +int main(int argc, char *argv[]) +{ + int c, i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode = -1; + struct wpa_params params; + struct wpa_global *global; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + iface_count = 1; + + wpa_supplicant_fd_workaround(); + + for (;;) { + c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNp:P:qtuvW"); + if (c < 0) + break; + switch (c) { + case 'b': + iface->bridge_ifname = optarg; + break; + case 'B': + params.daemonize++; + break; + case 'c': + iface->confname = optarg; + break; + case 'C': + iface->ctrl_interface = optarg; + break; + case 'D': + iface->driver = optarg; + break; + case 'd': +#ifdef CONFIG_NO_STDOUT_DEBUG + printf("Debugging disabled with " + "CONFIG_NO_STDOUT_DEBUG=y build time " + "option.\n"); + goto out; +#else /* CONFIG_NO_STDOUT_DEBUG */ + params.wpa_debug_level--; + break; +#endif /* CONFIG_NO_STDOUT_DEBUG */ +#ifdef CONFIG_DEBUG_FILE + case 'f': + params.wpa_debug_file_path = optarg; + break; +#endif /* CONFIG_DEBUG_FILE */ + case 'g': + params.ctrl_interface = optarg; + break; + case 'h': + usage(); + exitcode = 0; + goto out; + case 'i': + iface->ifname = optarg; + break; + case 'K': + params.wpa_debug_show_keys++; + break; + case 'L': + license(); + exitcode = 0; + goto out; + case 'p': + iface->driver_param = optarg; + break; + case 'P': + os_free(params.pid_file); + params.pid_file = os_rel2abs_path(optarg); + break; + case 'q': + params.wpa_debug_level++; + break; + case 't': + params.wpa_debug_timestamp++; + break; +#ifdef CONFIG_CTRL_IFACE_DBUS + case 'u': + params.dbus_ctrl_interface = 1; + break; +#endif /* CONFIG_CTRL_IFACE_DBUS */ + case 'v': + printf("%s\n", wpa_supplicant_version); + exitcode = 0; + goto out; + case 'W': + params.wait_for_monitor++; + break; + case 'N': + iface_count++; + iface = os_realloc(ifaces, iface_count * + sizeof(struct wpa_interface)); + if (iface == NULL) + goto out; + ifaces = iface; + iface = &ifaces[iface_count - 1]; + os_memset(iface, 0, sizeof(*iface)); + break; + default: + usage(); + exitcode = 0; + goto out; + } + } + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); + exitcode = -1; + goto out; + } + + for (i = 0; exitcode == 0 && i < iface_count; i++) { + if ((ifaces[i].confname == NULL && + ifaces[i].ctrl_interface == NULL) || + ifaces[i].ifname == NULL) { + if (iface_count == 1 && (params.ctrl_interface || + params.dbus_ctrl_interface)) + break; + usage(); + exitcode = -1; + break; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + +out: + os_free(ifaces); + os_free(params.pid_file); + + os_program_deinit(); + + return exitcode; +} diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c new file mode 100644 index 0000000000000..993338a9f900c --- /dev/null +++ b/wpa_supplicant/main_none.c @@ -0,0 +1,46 @@ +/* + * WPA Supplicant / Example program entrypoint + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +int main(int argc, char *argv[]) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_symbian.cpp b/wpa_supplicant/main_symbian.cpp new file mode 100644 index 0000000000000..4ff364be3ccae --- /dev/null +++ b/wpa_supplicant/main_symbian.cpp @@ -0,0 +1,48 @@ +/* + * WPA Supplicant / Program entrypoint for Symbian + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +extern "C" { +#include "common.h" +#include "wpa_supplicant_i.h" +} + +GLDEF_C TInt E32Main(void) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c new file mode 100644 index 0000000000000..19d9950ab34ea --- /dev/null +++ b/wpa_supplicant/main_winmain.c @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / WinMain() function for Windows-based applications + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +#ifdef _WIN32_WCE +#define CMDLINE LPWSTR +#else /* _WIN32_WCE */ +#define CMDLINE LPSTR +#endif /* _WIN32_WCE */ + + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + CMDLINE lpCmdLine, int nShowCmd) +{ + int i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode = -1; + struct wpa_params params; + struct wpa_global *global; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_MSGDUMP; + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + params.wpa_debug_show_keys = 1; + + iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + iface_count = 1; + + iface->confname = "default"; + iface->driver = "ndis"; + iface->ifname = ""; + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + for (i = 0; exitcode == 0 && i < iface_count; i++) { + if ((ifaces[i].confname == NULL && + ifaces[i].ctrl_interface == NULL) || + ifaces[i].ifname == NULL) { + if (iface_count == 1 && (params.ctrl_interface || + params.dbus_ctrl_interface)) + break; + exitcode = -1; + break; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_free(ifaces); + + os_program_deinit(); + + return exitcode; +} diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c new file mode 100644 index 0000000000000..4a46ed5f10491 --- /dev/null +++ b/wpa_supplicant/main_winsvc.c @@ -0,0 +1,464 @@ +/* + * WPA Supplicant / main() function for Win32 service + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * The root of wpa_supplicant configuration in registry is + * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global + * parameters and a 'interfaces' subkey with all the interface configuration + * (adapter to confname mapping). Each such mapping is a subkey that has + * 'adapter' and 'config' values. + * + * This program can be run either as a normal command line application, e.g., + * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need + * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After + * this, it can be started like any other Windows service (e.g., 'net start + * wpasvc') or it can be configured to start automatically through the Services + * tool in administrative tasks. The service can be unregistered with + * 'wpasvc.exe unreg'. + */ + +#include "includes.h" +#include <windows.h> + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "eloop.h" + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif +#ifndef WPASVC_DISPLAY_NAME +#define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service") +#endif +#ifndef WPASVC_DESCRIPTION +#define WPASVC_DESCRIPTION \ +TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality") +#endif + +static HANDLE kill_svc; + +static SERVICE_STATUS_HANDLE svc_status_handle; +static SERVICE_STATUS svc_status; + + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int read_interface(struct wpa_global *global, HKEY _hk, + const TCHAR *name) +{ + HKEY hk; +#define TBUFLEN 255 + TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN]; + DWORD buflen, val; + LONG ret; + struct wpa_interface iface; + int skip_on_error = 0; + + ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interface key\n"); + return -1; + } + + os_memset(&iface, 0, sizeof(iface)); + iface.driver = "ndis"; + + buflen = sizeof(ctrl_interface); + ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL, + (LPBYTE) ctrl_interface, &buflen); + if (ret == ERROR_SUCCESS) { + ctrl_interface[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(ctrl_interface); + printf("ctrl_interface[len=%d] '%s'\n", + (int) buflen, (char *) ctrl_interface); + iface.ctrl_interface = (char *) ctrl_interface; + } + + buflen = sizeof(adapter); + ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL, + (LPBYTE) adapter, &buflen); + if (ret == ERROR_SUCCESS) { + adapter[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(adapter); + printf("adapter[len=%d] '%s'\n", + (int) buflen, (char *) adapter); + iface.ifname = (char *) adapter; + } + + buflen = sizeof(config); + ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL, + (LPBYTE) config, &buflen); + if (ret == ERROR_SUCCESS) { + config[sizeof(config) - 1] = '\0'; + wpa_unicode2ascii_inplace(config); + printf("config[len=%d] '%s'\n", + (int) buflen, (char *) config); + iface.confname = (char *) config; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) + skip_on_error = val; + + RegCloseKey(hk); + + if (wpa_supplicant_add_iface(global, &iface) == NULL) { + if (skip_on_error) + wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to " + "initialization failure", iface.ifname); + else + return -1; + } + + return 0; +} + + +static int wpa_supplicant_thread(void) +{ + int exitcode; + struct wpa_params params; + struct wpa_global *global; + HKEY hk, ihk; + DWORD val, buflen, i; + LONG ret; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX, + 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant registry key\n"); + return -1; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_level = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_show_keys = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_timestamp = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) { + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + } + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS, + &ihk); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interfaces registry " + "key\n"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + printf("RegEnumKeyEx failed: 0x%x\n", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name); + if (read_interface(global, ihk, name) < 0) + exitcode = -1; + } + + RegCloseKey(ihk); + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_program_deinit(); + + return exitcode; +} + + +static DWORD svc_thread(LPDWORD param) +{ + int ret = wpa_supplicant_thread(); + + svc_status.dwCurrentState = SERVICE_STOPPED; + svc_status.dwWaitHint = 0; + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } + + return ret; +} + + +static int register_service(const TCHAR *exe) +{ + SC_HANDLE svc, scm; + SERVICE_DESCRIPTION sd; + + printf("Registering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + exe, NULL, NULL, NULL, NULL, NULL); + + if (!svc) { + printf("CreateService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + os_memset(&sd, 0, sizeof(sd)); + sd.lpDescription = WPASVC_DESCRIPTION; + if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) { + printf("ChangeServiceConfig2 failed: %d\n", + (int) GetLastError()); + /* This is not a fatal error, so continue anyway. */ + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + printf("Service registered successfully.\n"); + + return 0; +} + + +static int unregister_service(void) +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + printf("Unregistering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE); + if (!svc) { + printf("OpenService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) { + printf("Service currently active - stopping " + "service...\n"); + if (!ControlService(svc, SERVICE_CONTROL_STOP, + &status)) { + printf("ControlService failed: %d\n", + (int) GetLastError()); + } + Sleep(500); + } + } + + if (DeleteService(svc)) { + printf("Service unregistered successfully.\n"); + } else { + printf("DeleteService failed: %d\n", (int) GetLastError()); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return 0; +} + + +static void WINAPI service_ctrl_handler(DWORD control_code) +{ + switch (control_code) { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + svc_status.dwCurrentState = SERVICE_STOP_PENDING; + svc_status.dwWaitHint = 2000; + eloop_terminate(); + SetEvent(kill_svc); + break; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } +} + + +static void WINAPI service_start(DWORD argc, LPTSTR *argv) +{ + DWORD id; + + svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME, + service_ctrl_handler); + if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) { + printf("RegisterServiceCtrlHandler failed: %d\n", + (int) GetLastError()); + return; + } + + os_memset(&svc_status, 0, sizeof(svc_status)); + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + svc_status.dwCurrentState = SERVICE_START_PENDING; + svc_status.dwWaitHint = 1000; + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + kill_svc = CreateEvent(0, TRUE, FALSE, 0); + if (!kill_svc) { + printf("CreateEvent failed: %d\n", (int) GetLastError()); + return; + } + + if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id) + == 0) { + printf("CreateThread failed: %d\n", (int) GetLastError()); + return; + } + + if (svc_status.dwCurrentState == SERVICE_START_PENDING) { + svc_status.dwCurrentState = SERVICE_RUNNING; + svc_status.dwWaitHint = 0; + svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + /* wait until service gets killed */ + WaitForSingleObject(kill_svc, INFINITE); +} + + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY dt[] = { + { WPASVC_NAME, service_start }, + { NULL, NULL } + }; + + if (argc > 1) { + if (os_strcmp(argv[1], "reg") == 0) { + TCHAR *path; + int ret; + + if (argc < 3) { + path = os_malloc(MAX_PATH * sizeof(TCHAR)); + if (path == NULL) + return -1; + if (!GetModuleFileName(NULL, path, MAX_PATH)) { + printf("GetModuleFileName failed: " + "%d\n", (int) GetLastError()); + os_free(path); + return -1; + } + } else { + path = wpa_strdup_tchar(argv[2]); + if (path == NULL) + return -1; + } + ret = register_service(path); + os_free(path); + return ret; + } else if (os_strcmp(argv[1], "unreg") == 0) { + return unregister_service(); + } else if (os_strcmp(argv[1], "app") == 0) { + return wpa_supplicant_thread(); + } + } + + if (!StartServiceCtrlDispatcher(dt)) { + printf("StartServiceCtrlDispatcher failed: %d\n", + (int) GetLastError()); + } + + return 0; +} diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c new file mode 100644 index 0000000000000..0c630673a09a1 --- /dev/null +++ b/wpa_supplicant/mlme.c @@ -0,0 +1,2994 @@ +/* + * WPA Supplicant - Client mode MLME + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "wpa.h" +#include "drivers/driver.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" +#include "mlme.h" + + +/* Timeouts and intervals in milliseconds */ +#define IEEE80211_AUTH_TIMEOUT (200) +#define IEEE80211_AUTH_MAX_TRIES 3 +#define IEEE80211_ASSOC_TIMEOUT (200) +#define IEEE80211_ASSOC_MAX_TRIES 3 +#define IEEE80211_MONITORING_INTERVAL (2000) +#define IEEE80211_PROBE_INTERVAL (60000) +#define IEEE80211_RETRY_AUTH_INTERVAL (1000) +#define IEEE80211_SCAN_INTERVAL (2000) +#define IEEE80211_SCAN_INTERVAL_SLOW (15000) +#define IEEE80211_IBSS_JOIN_TIMEOUT (20000) + +#define IEEE80211_PROBE_DELAY (33) +#define IEEE80211_CHANNEL_TIME (33) +#define IEEE80211_PASSIVE_CHANNEL_TIME (200) +#define IEEE80211_SCAN_RESULT_EXPIRE (10000) +#define IEEE80211_IBSS_MERGE_INTERVAL (30000) +#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000) + +#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 + + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + + +struct ieee80211_sta_bss { + struct ieee80211_sta_bss *next; + struct ieee80211_sta_bss *hnext; + + u8 bssid[ETH_ALEN]; + u8 ssid[MAX_SSID_LEN]; + size_t ssid_len; + u16 capability; /* host byte order */ + int hw_mode; + int channel; + int freq; + int rssi; + u8 *ie; + size_t ie_len; + u8 *wpa_ie; + size_t wpa_ie_len; + u8 *rsn_ie; + size_t rsn_ie_len; + u8 *wmm_ie; + size_t wmm_ie_len; + u8 *mdie; + size_t mdie_len; +#define IEEE80211_MAX_SUPP_RATES 32 + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; + int beacon_int; + u64 timestamp; + + int probe_resp; + struct os_time last_update; +}; + + +static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, + const u8 *dst, + const u8 *ssid, size_t ssid_len); +static struct ieee80211_sta_bss * +ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid); +static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s); +static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s); +static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx); +static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx); + + +static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s, + wpa_hw_mode phymode, int chan, + int freq) +{ + size_t i; + struct wpa_hw_modes *mode; + + for (i = 0; i < wpa_s->mlme.num_modes; i++) { + mode = &wpa_s->mlme.modes[i]; + if (mode->mode == phymode) { + wpa_s->mlme.curr_rates = mode->rates; + wpa_s->mlme.num_curr_rates = mode->num_rates; + break; + } + } + + return wpa_drv_set_channel(wpa_s, phymode, chan, freq); +} + + + +#if 0 /* FIX */ +static int ecw2cw(int ecw) +{ + int cw = 1; + while (ecw > 0) { + cw <<= 1; + ecw--; + } + return cw - 1; +} +#endif + + +static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s, + u8 *wmm_param, size_t wmm_param_len) +{ + size_t left; + int count; + u8 *pos; + + if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) + return; + count = wmm_param[6] & 0x0f; + if (count == wpa_s->mlme.wmm_last_param_set) + return; + wpa_s->mlme.wmm_last_param_set = count; + + pos = wmm_param + 8; + left = wmm_param_len - 8; + +#if 0 /* FIX */ + wmm_acm = 0; + for (; left >= 4; left -= 4, pos += 4) { + int aci = (pos[0] >> 5) & 0x03; + int acm = (pos[0] >> 4) & 0x01; + int queue; + + switch (aci) { + case 1: + queue = IEEE80211_TX_QUEUE_DATA3; + if (acm) + wmm_acm |= BIT(1) | BIT(2); + break; + case 2: + queue = IEEE80211_TX_QUEUE_DATA1; + if (acm) + wmm_acm |= BIT(4) | BIT(5); + break; + case 3: + queue = IEEE80211_TX_QUEUE_DATA0; + if (acm) + wmm_acm |= BIT(6) | BIT(7); + break; + case 0: + default: + queue = IEEE80211_TX_QUEUE_DATA2; + if (acm) + wpa_s->mlme.wmm_acm |= BIT(0) | BIT(3); + break; + } + + params.aifs = pos[0] & 0x0f; + params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); + params.cw_min = ecw2cw(pos[1] & 0x0f); + /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ + params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; + wpa_printf(MSG_DEBUG, "MLME: WMM queue=%d aci=%d acm=%d " + "aifs=%d cWmin=%d cWmax=%d burst=%d", + queue, aci, acm, params.aifs, params.cw_min, + params.cw_max, params.burst_time); + /* TODO: handle ACM (block TX, fallback to next lowest allowed + * AC for now) */ + if (local->hw->conf_tx(local->mdev, queue, ¶ms)) { + wpa_printf(MSG_DEBUG, "MLME: failed to set TX queue " + "parameters for queue %d", queue); + } + } +#endif +} + + +static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc) +{ + if (wpa_s->mlme.associated == assoc && !assoc) + return; + + wpa_s->mlme.associated = assoc; + + if (assoc) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + wpa_s->mlme.prev_bssid_set = 1; + os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); + data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies; + data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len; + data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies; + data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len; + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); + } else { + wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); + } + os_get_time(&wpa_s->mlme.last_probe); +} + + +static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf, + size_t len) +{ + return wpa_drv_send_mlme(wpa_s, buf, len); +} + + +static void ieee80211_send_auth(struct wpa_supplicant *wpa_s, + int transaction, u8 *extra, size_t extra_len, + int encrypt) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_malloc(sizeof(*mgmt) + 6 + extra_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "auth frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24 + 6; + os_memset(mgmt, 0, 24 + 6); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + if (encrypt) + mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg); + mgmt->u.auth.auth_transaction = host_to_le16(transaction); + wpa_s->mlme.auth_transaction = transaction + 1; + mgmt->u.auth.status_code = host_to_le16(0); + if (extra) { + os_memcpy(buf + len, extra, extra_len); + len += extra_len; + } + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms) +{ + eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); + eloop_register_timeout(ms / 1000, 1000 * (ms % 1000), + ieee80211_sta_timer, wpa_s, NULL); +} + + +static void ieee80211_authenticate(struct wpa_supplicant *wpa_s) +{ + u8 *extra; + size_t extra_len; + + wpa_s->mlme.auth_tries++; + if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) { + wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR + " timed out", MAC2STR(wpa_s->bssid)); + return; + } + + wpa_s->mlme.state = IEEE80211_AUTHENTICATE; + wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR, + MAC2STR(wpa_s->bssid)); + + extra = NULL; + extra_len = 0; + +#ifdef CONFIG_IEEE80211R + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.ft_ies) { + struct ieee80211_sta_bss *bss; + struct rsn_mdie *mdie = NULL; + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss && bss->mdie_len >= 2 + sizeof(*mdie)) + mdie = (struct rsn_mdie *) (bss->mdie + 2); + if (mdie && + os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md, + MOBILITY_DOMAIN_ID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "MLME: Trying to use FT " + "over-the-air"); + wpa_s->mlme.auth_alg = WLAN_AUTH_FT; + extra = wpa_s->mlme.ft_ies; + extra_len = wpa_s->mlme.ft_ies_len; + } + } +#endif /* CONFIG_IEEE80211R */ + + ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0); + + ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT); +} + + +static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_mgmt *mgmt; + u8 *pos, *ies, *buf; + int i, len; + u16 capab; + struct ieee80211_sta_bss *bss; + int wmm = 0; + size_t blen, buflen; + + if (wpa_s->mlme.curr_rates == NULL) { + wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc"); + return; + } + + buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len + + wpa_s->mlme.ssid_len; +#ifdef CONFIG_IEEE80211R + if (wpa_s->mlme.ft_ies) + buflen += wpa_s->mlme.ft_ies_len; +#endif /* CONFIG_IEEE80211R */ + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "assoc frame"); + return; + } + blen = 0; + + capab = wpa_s->mlme.capab; + if (wpa_s->mlme.phymode == WPA_MODE_IEEE80211G) { + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE; + } + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss) { + if (bss->capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + if (bss->wmm_ie) { + wmm = 1; + } + } + + mgmt = (struct ieee80211_mgmt *) buf; + blen += 24; + os_memset(mgmt, 0, 24); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + + if (wpa_s->mlme.prev_bssid_set) { + blen += 10; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_REASSOC_REQ); + mgmt->u.reassoc_req.capab_info = host_to_le16(capab); + mgmt->u.reassoc_req.listen_interval = host_to_le16(1); + os_memcpy(mgmt->u.reassoc_req.current_ap, + wpa_s->mlme.prev_bssid, + ETH_ALEN); + } else { + blen += 4; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ASSOC_REQ); + mgmt->u.assoc_req.capab_info = host_to_le16(capab); + mgmt->u.assoc_req.listen_interval = host_to_le16(1); + } + + /* SSID */ + ies = pos = buf + blen; + blen += 2 + wpa_s->mlme.ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = wpa_s->mlme.ssid_len; + os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + + len = wpa_s->mlme.num_curr_rates; + if (len > 8) + len = 8; + pos = buf + blen; + blen += len + 2; + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = len; + for (i = 0; i < len; i++) { + int rate = wpa_s->mlme.curr_rates[i].rate; + *pos++ = (u8) (rate / 5); + } + + if (wpa_s->mlme.num_curr_rates > len) { + pos = buf + blen; + blen += wpa_s->mlme.num_curr_rates - len + 2; + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = wpa_s->mlme.num_curr_rates - len; + for (i = len; i < wpa_s->mlme.num_curr_rates; i++) { + int rate = wpa_s->mlme.curr_rates[i].rate; + *pos++ = (u8) (rate / 5); + } + } + + if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) { + pos = buf + blen; + blen += wpa_s->mlme.extra_ie_len; + os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len); + } + +#ifdef CONFIG_IEEE80211R + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.auth_alg != WLAN_AUTH_FT && + bss && bss->mdie && + bss->mdie_len >= 2 + sizeof(struct rsn_mdie) && + bss->mdie[1] >= sizeof(struct rsn_mdie)) { + pos = buf + blen; + blen += 2 + sizeof(struct rsn_mdie); + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(struct rsn_mdie); + os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + *pos++ = 0; /* FIX: copy from the target AP's MDIE */ + } + + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) { + pos = buf + blen; + os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len); + pos += wpa_s->mlme.ft_ies_len; + blen += wpa_s->mlme.ft_ies_len; + } +#endif /* CONFIG_IEEE80211R */ + + if (wmm && wpa_s->mlme.wmm_enabled) { + pos = buf + blen; + blen += 9; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *pos++ = 0x50; + *pos++ = 0xf2; + *pos++ = 2; /* WME */ + *pos++ = 0; /* WME info */ + *pos++ = 1; /* WME ver */ + *pos++ = 0; + } + + os_free(wpa_s->mlme.assocreq_ies); + wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies; + wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len); + if (wpa_s->mlme.assocreq_ies) { + os_memcpy(wpa_s->mlme.assocreq_ies, ies, + wpa_s->mlme.assocreq_ies_len); + } + + ieee80211_sta_tx(wpa_s, buf, blen); + os_free(buf); +} + + +static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_zalloc(sizeof(*mgmt)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "deauth frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + len += 2; + mgmt->u.deauth.reason_code = host_to_le16(reason); + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_zalloc(sizeof(*mgmt)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "disassoc frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + len += 2; + mgmt->u.disassoc.reason_code = host_to_le16(reason); + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + int res = 0; + + if (wpa_s->mlme.mixed_cell || + wpa_s->mlme.key_mgmt != KEY_MGMT_NONE) + return 0; + + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss == NULL) + return 0; + + if (ieee80211_sta_wep_configured(wpa_s) != + !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) + res = 1; + + return res; +} + + +static void ieee80211_associate(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.assoc_tries++; + if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { + wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR + " timed out", MAC2STR(wpa_s->bssid)); + return; + } + + wpa_s->mlme.state = IEEE80211_ASSOCIATE; + wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR, + MAC2STR(wpa_s->bssid)); + if (ieee80211_privacy_mismatch(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy " + "configuration and mixed-cell disabled - abort " + "association"); + return; + } + + ieee80211_send_assoc(wpa_s); + + ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT); +} + + +static void ieee80211_associated(struct wpa_supplicant *wpa_s) +{ + int disassoc; + + /* TODO: start monitoring current AP signal quality and number of + * missed beacons. Scan other channels every now and then and search + * for better APs. */ + /* TODO: remove expired BSSes */ + + wpa_s->mlme.state = IEEE80211_ASSOCIATED; + +#if 0 /* FIX */ + sta = sta_info_get(local, wpa_s->bssid); + if (sta == NULL) { + wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR, + MAC2STR(wpa_s->bssid)); + disassoc = 1; + } else { + disassoc = 0; + if (time_after(jiffies, + sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + if (wpa_s->mlme.probereq_poll) { + wpa_printf(MSG_DEBUG "MLME: No ProbeResp from " + "current AP " MACSTR " - assume " + "out of range", + MAC2STR(wpa_s->bssid)); + disassoc = 1; + } else { + ieee80211_send_probe_req( + wpa_s->bssid, + wpa_s->mlme.scan_ssid, + wpa_s->mlme.scan_ssid_len); + wpa_s->mlme.probereq_poll = 1; + } + } else { + wpa_s->mlme.probereq_poll = 0; + if (time_after(jiffies, wpa_s->mlme.last_probe + + IEEE80211_PROBE_INTERVAL)) { + wpa_s->mlme.last_probe = jiffies; + ieee80211_send_probe_req(wpa_s->bssid, + wpa_s->mlme.ssid, + wpa_s->mlme.ssid_len); + } + } + sta_info_release(local, sta); + } +#else + disassoc = 0; +#endif + if (disassoc) { + wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); + ieee80211_reschedule_timer(wpa_s, + IEEE80211_MONITORING_INTERVAL + + 30000); + } else { + ieee80211_reschedule_timer(wpa_s, + IEEE80211_MONITORING_INTERVAL); + } +} + + +static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, + const u8 *dst, + const u8 *ssid, size_t ssid_len) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + u8 *pos, *supp_rates; + u8 *esupp_rates = NULL; + int i; + + buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "probe request"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memset(mgmt, 0, 24); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_REQ); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + if (dst) { + os_memcpy(mgmt->da, dst, ETH_ALEN); + os_memcpy(mgmt->bssid, dst, ETH_ALEN); + } else { + os_memset(mgmt->da, 0xff, ETH_ALEN); + os_memset(mgmt->bssid, 0xff, ETH_ALEN); + } + pos = buf + len; + len += 2 + ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + + supp_rates = buf + len; + len += 2; + supp_rates[0] = WLAN_EID_SUPP_RATES; + supp_rates[1] = 0; + for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) { + struct wpa_rate_data *rate = &wpa_s->mlme.curr_rates[i]; + if (esupp_rates) { + pos = buf + len; + len++; + esupp_rates[1]++; + } else if (supp_rates[1] == 8) { + esupp_rates = pos; + esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; + esupp_rates[1] = 1; + pos = &esupp_rates[2]; + len += 3; + } else { + pos = buf + len; + len++; + supp_rates[1]++; + } + *pos++ = rate->rate / 5; + } + + if (wpa_s->mlme.extra_probe_ie) { + os_memcpy(pos, wpa_s->mlme.extra_probe_ie, + wpa_s->mlme.extra_probe_ie_len); + len += wpa_s->mlme.extra_probe_ie_len; + } + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + if (sdata == NULL || sdata->default_key == NULL || + sdata->default_key->alg != ALG_WEP) + return 0; + return 1; +#else + return 0; +#endif +} + + +static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "MLME: authenticated"); + wpa_s->mlme.authenticated = 1; + ieee80211_associate(wpa_s); +} + + +static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u8 *pos; + struct ieee802_11_elems elems; + + wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge"); + pos = mgmt->u.auth.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) + == ParseFailed) { + wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)"); + return; + } + if (elems.challenge == NULL) { + wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key " + "auth frame"); + return; + } + ieee80211_send_auth(wpa_s, 3, elems.challenge - 2, + elems.challenge_len + 2, 1); +} + + +static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + u16 auth_alg, auth_transaction, status_code; + int adhoc; + + adhoc = ssid && ssid->mode == 1; + + if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from " MACSTR ", but not in authenticate state - " + "ignored", MAC2STR(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + + wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR + " (alg=%d transaction=%d status=%d)", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code); + + if (adhoc) { + /* IEEE 802.11 standard does not require authentication in IBSS + * networks and most implementations do not seem to use it. + * However, try to reply to authentication attempts if someone + * has actually implemented this. + * TODO: Could implement shared key authentication. */ + if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { + wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS " + "authentication frame (alg=%d " + "transaction=%d)", + auth_alg, auth_transaction); + return; + } + ieee80211_send_auth(wpa_s, 2, NULL, 0, 0); + } + + if (auth_alg != wpa_s->mlme.auth_alg || + auth_transaction != wpa_s->mlme.auth_transaction) { + wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame " + "(alg=%d transaction=%d)", + auth_alg, auth_transaction); + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "MLME: AP denied authentication " + "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg, + status_code); + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + const int num_algs = 3; + u8 algs[num_algs]; + int i, pos; + algs[0] = algs[1] = algs[2] = 0xff; + if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_OPEN) + algs[0] = WLAN_AUTH_OPEN; + if (wpa_s->mlme.auth_algs & + IEEE80211_AUTH_ALG_SHARED_KEY) + algs[1] = WLAN_AUTH_SHARED_KEY; + if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_LEAP) + algs[2] = WLAN_AUTH_LEAP; + if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN) + pos = 0; + else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY) + pos = 1; + else + pos = 2; + for (i = 0; i < num_algs; i++) { + pos++; + if (pos >= num_algs) + pos = 0; + if (algs[pos] == wpa_s->mlme.auth_alg || + algs[pos] == 0xff) + continue; + if (algs[pos] == WLAN_AUTH_SHARED_KEY && + !ieee80211_sta_wep_configured(wpa_s)) + continue; + wpa_s->mlme.auth_alg = algs[pos]; + wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d " + "for next try", + wpa_s->mlme.auth_alg); + break; + } + } + return; + } + + switch (wpa_s->mlme.auth_alg) { + case WLAN_AUTH_OPEN: + case WLAN_AUTH_LEAP: + ieee80211_auth_completed(wpa_s); + break; + case WLAN_AUTH_SHARED_KEY: + if (wpa_s->mlme.auth_transaction == 4) + ieee80211_auth_completed(wpa_s); + else + ieee80211_auth_challenge(wpa_s, mgmt, len, + rx_status); + break; +#ifdef CONFIG_IEEE80211R + case WLAN_AUTH_FT: + { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.ft_ies.ies = mgmt->u.auth.variable; + data.ft_ies.ies_len = len - + (mgmt->u.auth.variable - (u8 *) mgmt); + os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN); + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); + ieee80211_auth_completed(wpa_s); + break; + } +#endif /* CONFIG_IEEE80211R */ + } +} + + +static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u16 reason_code; + + if (len < 24 + 2) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR + " (reason=%d)", MAC2STR(mgmt->sa), reason_code); + + if (wpa_s->mlme.authenticated) + wpa_printf(MSG_DEBUG, "MLME: deauthenticated"); + + if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE || + wpa_s->mlme.state == IEEE80211_ASSOCIATE || + wpa_s->mlme.state == IEEE80211_ASSOCIATED) { + wpa_s->mlme.state = IEEE80211_AUTHENTICATE; + ieee80211_reschedule_timer(wpa_s, + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_associated(wpa_s, 0); + wpa_s->mlme.authenticated = 0; +} + + +static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u16 reason_code; + + if (len < 24 + 2) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: disassociation frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + reason_code = le_to_host16(mgmt->u.disassoc.reason_code); + + wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR + " (reason=%d)", MAC2STR(mgmt->sa), reason_code); + + if (wpa_s->mlme.associated) + wpa_printf(MSG_DEBUG, "MLME: disassociated"); + + if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) { + wpa_s->mlme.state = IEEE80211_ASSOCIATE; + ieee80211_reschedule_timer(wpa_s, + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_associated(wpa_s, 0); +} + + +static int ieee80211_ft_assoc_resp(struct wpa_supplicant *wpa_s, + struct ieee802_11_elems *elems) +{ +#ifdef CONFIG_IEEE80211R + const u8 *mobility_domain = NULL; + const u8 *r0kh_id = NULL; + size_t r0kh_id_len = 0; + const u8 *r1kh_id = NULL; + struct rsn_ftie *hdr; + const u8 *pos, *end; + + if (elems->mdie && elems->mdie_len >= MOBILITY_DOMAIN_ID_LEN) + mobility_domain = elems->mdie; + if (elems->ftie && elems->ftie_len >= sizeof(struct rsn_ftie)) { + end = elems->ftie + elems->ftie_len; + hdr = (struct rsn_ftie *) elems->ftie; + pos = (const u8 *) (hdr + 1); + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == FTIE_SUBELEM_R1KH_ID && + pos[1] == FT_R1KH_ID_LEN) + r1kh_id = pos + 2; + else if (pos[0] == FTIE_SUBELEM_R0KH_ID && + pos[1] >= 1 && pos[1] <= FT_R0KH_ID_MAX_LEN) { + r0kh_id = pos + 2; + r0kh_id_len = pos[1]; + } + pos += 2 + pos[1]; + } + } + return wpa_sm_set_ft_params(wpa_s->wpa, mobility_domain, r0kh_id, + r0kh_id_len, r1kh_id); +#else /* CONFIG_IEEE80211R */ + return 0; +#endif /* CONFIG_IEEE80211R */ +} + + +static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status, + int reassoc) +{ + u8 rates[32]; + size_t rates_len; + u16 capab_info, status_code, aid; + struct ieee802_11_elems elems; + u8 *pos; + + /* AssocResp and ReassocResp have identical structure, so process both + * of them in this function. */ + + if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) { + wpa_printf(MSG_DEBUG, "MLME: association frame received from " + MACSTR ", but not in associate state - ignored", + MAC2STR(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: association frame received from " + "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " + "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info); + status_code = le_to_host16(mgmt->u.assoc_resp.status_code); + aid = le_to_host16(mgmt->u.assoc_resp.aid); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 " + "not set", aid); + aid &= ~(BIT(15) | BIT(14)); + + wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR + " (capab=0x%x status=%d aid=%d)", + reassoc ? "Rea" : "A", MAC2STR(mgmt->sa), + capab_info, status_code, aid); + + pos = mgmt->u.assoc_resp.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) + == ParseFailed) { + wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp"); + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)", + status_code); +#ifdef CONFIG_IEEE80211W + if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && + elems.timeout_int && elems.timeout_int_len == 5 && + elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { + u32 tu, ms; + tu = WPA_GET_LE32(elems.timeout_int + 1); + ms = tu * 1024 / 1000; + wpa_printf(MSG_DEBUG, "MLME: AP rejected association " + "temporarily; comeback duration %u TU " + "(%u ms)", tu, ms); + if (ms > IEEE80211_ASSOC_TIMEOUT) { + wpa_printf(MSG_DEBUG, "MLME: Update timer " + "based on comeback duration"); + ieee80211_reschedule_timer(wpa_s, ms); + } + } +#endif /* CONFIG_IEEE80211W */ + return; + } + + if (elems.supp_rates == NULL) { + wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in " + "AssocResp"); + return; + } + + if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "MLME: AP tried to use " + "association, not reassociation, response " + "with FT"); + return; + } + if (wpa_ft_validate_reassoc_resp( + wpa_s->wpa, pos, len - (pos - (u8 *) mgmt), + mgmt->sa) < 0) { + wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc" + "Resp failed"); + return; + } + } else if (ieee80211_ft_assoc_resp(wpa_s, &elems) < 0) + return; + + wpa_printf(MSG_DEBUG, "MLME: associated"); + wpa_s->mlme.aid = aid; + wpa_s->mlme.ap_capab = capab_info; + + os_free(wpa_s->mlme.assocresp_ies); + wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt); + wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len); + if (wpa_s->mlme.assocresp_ies) { + os_memcpy(wpa_s->mlme.assocresp_ies, pos, + wpa_s->mlme.assocresp_ies_len); + } + + ieee80211_set_associated(wpa_s, 1); + + rates_len = elems.supp_rates_len; + if (rates_len > sizeof(rates)) + rates_len = sizeof(rates); + os_memcpy(rates, elems.supp_rates, rates_len); + if (elems.ext_supp_rates) { + size_t _len = elems.ext_supp_rates_len; + if (_len > sizeof(rates) - rates_len) + _len = sizeof(rates) - rates_len; + os_memcpy(rates + rates_len, elems.ext_supp_rates, _len); + rates_len += _len; + } + + if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the " + "netstack"); + } + if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) < + 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the " + "netstack"); + } + + /* Remove STA entry before adding a new one just in case to avoid + * problems with existing configuration (e.g., keys). */ + wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid); + if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the " + "netstack"); + } + +#if 0 /* FIX? */ + sta->assoc_ap = 1; + + if (elems.wme && wpa_s->mlme.wmm_enabled) { + sta->flags |= WLAN_STA_WME; + ieee80211_sta_wmm_params(wpa_s, elems.wme, elems.wme_len); + } +#endif + + ieee80211_associated(wpa_s); +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; + wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss; +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_sta_bss *b, *prev = NULL; + b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; + while (b) { + if (b == bss) { + if (prev == NULL) { + wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] + = bss->hnext; + } else { + prev->hnext = bss->hnext; + } + break; + } + prev = b; + b = b->hnext; + } +} + + +static struct ieee80211_sta_bss * +ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct ieee80211_sta_bss *bss; + + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return NULL; + os_memcpy(bss->bssid, bssid, ETH_ALEN); + + /* TODO: order by RSSI? */ + bss->next = wpa_s->mlme.sta_bss_list; + wpa_s->mlme.sta_bss_list = bss; + __ieee80211_bss_hash_add(wpa_s, bss); + return bss; +} + + +static struct ieee80211_sta_bss * +ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct ieee80211_sta_bss *bss; + + bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)]; + while (bss) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + break; + bss = bss->hnext; + } + return bss; +} + + +static void ieee80211_bss_free(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + __ieee80211_bss_hash_del(wpa_s, bss); + os_free(bss->ie); + os_free(bss->wpa_ie); + os_free(bss->rsn_ie); + os_free(bss->wmm_ie); + os_free(bss->mdie); + os_free(bss); +} + + +static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss, *prev; + + bss = wpa_s->mlme.sta_bss_list; + wpa_s->mlme.sta_bss_list = NULL; + while (bss) { + prev = bss; + bss = bss->next; + ieee80211_bss_free(wpa_s, prev); + } +} + + +static void ieee80211_bss_info(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status, + int beacon) +{ + struct ieee802_11_elems elems; + size_t baselen; + int channel, invalid = 0, clen; + struct ieee80211_sta_bss *bss; + u64 timestamp; + u8 *pos, *ie_pos; + size_t ie_len; + + if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN)) + return; /* ignore ProbeResp to foreign address */ + +#if 0 + wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR, + beacon ? "Beacon" : "Probe Response", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); +#endif + + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + pos = mgmt->u.beacon.timestamp; + timestamp = WPA_GET_LE64(pos); + +#if 0 /* FIX */ + if (local->conf.mode == IW_MODE_ADHOC && beacon && + os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) { +#ifdef IEEE80211_IBSS_DEBUG + static unsigned long last_tsf_debug = 0; + u64 tsf; + if (local->hw->get_tsf) + tsf = local->hw->get_tsf(local->mdev); + else + tsf = -1LLU; + if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { + wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID=" + MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld " + "@%ld", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), + tsf, timestamp, tsf - timestamp, jiffies); + last_tsf_debug = jiffies; + } +#endif /* IEEE80211_IBSS_DEBUG */ + } +#endif + + ie_pos = mgmt->u.beacon.variable; + ie_len = len - baselen; + if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed) + invalid = 1; + +#if 0 /* FIX */ + if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates && + os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 && + (sta = sta_info_get(local, mgmt->sa))) { + struct ieee80211_rate *rates; + size_t num_rates; + u32 supp_rates, prev_rates; + int i, j, oper_mode; + + rates = local->curr_rates; + num_rates = local->num_curr_rates; + oper_mode = wpa_s->mlme.sta_scanning ? + local->scan_oper_phymode : local->conf.phymode; + for (i = 0; i < local->hw->num_modes; i++) { + struct ieee80211_hw_modes *mode = &local->hw->modes[i]; + if (oper_mode == mode->mode) { + rates = mode->rates; + num_rates = mode->num_rates; + break; + } + } + + supp_rates = 0; + for (i = 0; i < elems.supp_rates_len + + elems.ext_supp_rates_len; i++) { + u8 rate = 0; + int own_rate; + if (i < elems.supp_rates_len) + rate = elems.supp_rates[i]; + else if (elems.ext_supp_rates) + rate = elems.ext_supp_rates + [i - elems.supp_rates_len]; + own_rate = 5 * (rate & 0x7f); + if (oper_mode == MODE_ATHEROS_TURBO) + own_rate *= 2; + for (j = 0; j < num_rates; j++) + if (rates[j].rate == own_rate) + supp_rates |= BIT(j); + } + + prev_rates = sta->supp_rates; + sta->supp_rates &= supp_rates; + if (sta->supp_rates == 0) { + /* No matching rates - this should not really happen. + * Make sure that at least one rate is marked + * supported to avoid issues with TX rate ctrl. */ + sta->supp_rates = wpa_s->mlme.supp_rates_bits; + } + if (sta->supp_rates != prev_rates) { + wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set " + "for " MACSTR " based on beacon info " + "(0x%x & 0x%x -> 0x%x)", + MAC2STR(sta->addr), prev_rates, + supp_rates, sta->supp_rates); + } + sta_info_release(local, sta); + } +#endif + + if (elems.ssid == NULL) + return; + + if (elems.ds_params && elems.ds_params_len == 1) + channel = elems.ds_params[0]; + else + channel = rx_status->channel; + + bss = ieee80211_bss_get(wpa_s, mgmt->bssid); + if (bss == NULL) { + bss = ieee80211_bss_add(wpa_s, mgmt->bssid); + if (bss == NULL) + return; + } else { +#if 0 + /* TODO: order by RSSI? */ + spin_lock_bh(&local->sta_bss_lock); + list_move_tail(&bss->list, &local->sta_bss_list); + spin_unlock_bh(&local->sta_bss_lock); +#endif + } + + if (bss->probe_resp && beacon) { + /* Do not allow beacon to override data from Probe Response. */ + return; + } + + bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); + bss->capability = le_to_host16(mgmt->u.beacon.capab_info); + + if (bss->ie == NULL || bss->ie_len < ie_len) { + os_free(bss->ie); + bss->ie = os_malloc(ie_len); + } + if (bss->ie) { + os_memcpy(bss->ie, ie_pos, ie_len); + bss->ie_len = ie_len; + } + + if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) { + os_memcpy(bss->ssid, elems.ssid, elems.ssid_len); + bss->ssid_len = elems.ssid_len; + } + + bss->supp_rates_len = 0; + if (elems.supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.supp_rates_len) + clen = elems.supp_rates_len; + os_memcpy(&bss->supp_rates[bss->supp_rates_len], + elems.supp_rates, clen); + bss->supp_rates_len += clen; + } + if (elems.ext_supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.ext_supp_rates_len) + clen = elems.ext_supp_rates_len; + os_memcpy(&bss->supp_rates[bss->supp_rates_len], + elems.ext_supp_rates, clen); + bss->supp_rates_len += clen; + } + + if (elems.wpa_ie && + (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len || + os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) { + os_free(bss->wpa_ie); + bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2); + if (bss->wpa_ie) { + os_memcpy(bss->wpa_ie, elems.wpa_ie - 2, + elems.wpa_ie_len + 2); + bss->wpa_ie_len = elems.wpa_ie_len + 2; + } else + bss->wpa_ie_len = 0; + } else if (!elems.wpa_ie && bss->wpa_ie) { + os_free(bss->wpa_ie); + bss->wpa_ie = NULL; + bss->wpa_ie_len = 0; + } + + if (elems.rsn_ie && + (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len || + os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) { + os_free(bss->rsn_ie); + bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2); + if (bss->rsn_ie) { + os_memcpy(bss->rsn_ie, elems.rsn_ie - 2, + elems.rsn_ie_len + 2); + bss->rsn_ie_len = elems.rsn_ie_len + 2; + } else + bss->rsn_ie_len = 0; + } else if (!elems.rsn_ie && bss->rsn_ie) { + os_free(bss->rsn_ie); + bss->rsn_ie = NULL; + bss->rsn_ie_len = 0; + } + + if (elems.wme && + (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wme_len || + os_memcmp(bss->wmm_ie, elems.wme, elems.wme_len))) { + os_free(bss->wmm_ie); + bss->wmm_ie = os_malloc(elems.wme_len + 2); + if (bss->wmm_ie) { + os_memcpy(bss->wmm_ie, elems.wme - 2, + elems.wme_len + 2); + bss->wmm_ie_len = elems.wme_len + 2; + } else + bss->wmm_ie_len = 0; + } else if (!elems.wme && bss->wmm_ie) { + os_free(bss->wmm_ie); + bss->wmm_ie = NULL; + bss->wmm_ie_len = 0; + } + +#ifdef CONFIG_IEEE80211R + if (elems.mdie && + (bss->mdie == NULL || bss->mdie_len != elems.mdie_len || + os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) { + os_free(bss->mdie); + bss->mdie = os_malloc(elems.mdie_len + 2); + if (bss->mdie) { + os_memcpy(bss->mdie, elems.mdie - 2, + elems.mdie_len + 2); + bss->mdie_len = elems.mdie_len + 2; + } else + bss->mdie_len = 0; + } else if (!elems.mdie && bss->mdie) { + os_free(bss->mdie); + bss->mdie = NULL; + bss->mdie_len = 0; + } +#endif /* CONFIG_IEEE80211R */ + + bss->hw_mode = wpa_s->mlme.phymode; + bss->channel = channel; + bss->freq = wpa_s->mlme.freq; + if (channel != wpa_s->mlme.channel && + (wpa_s->mlme.phymode == WPA_MODE_IEEE80211G || + wpa_s->mlme.phymode == WPA_MODE_IEEE80211B) && + channel >= 1 && channel <= 14) { + static const int freq_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + /* IEEE 802.11g/b mode can receive packets from neighboring + * channels, so map the channel into frequency. */ + bss->freq = freq_list[channel - 1]; + } + bss->timestamp = timestamp; + os_get_time(&bss->last_update); + bss->rssi = rx_status->ssi; + if (!beacon) + bss->probe_resp++; +} + + +static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0); +} + + +static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + int use_protection; + size_t baselen; + struct ieee802_11_elems elems; + + ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1); + + if (!wpa_s->mlme.associated || + os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) + return; + + /* Process beacon from the current BSS */ + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, + &elems, 0) == ParseFailed) + return; + + use_protection = 0; + if (elems.erp_info && elems.erp_info_len >= 1) { + use_protection = + (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; + } + + if (use_protection != !!wpa_s->mlme.use_protection) { + wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR + ")", + use_protection ? "enabled" : "disabled", + MAC2STR(wpa_s->bssid)); + wpa_s->mlme.use_protection = use_protection ? 1 : 0; + wpa_s->mlme.cts_protect_erp_frames = use_protection; + } + + if (elems.wme && wpa_s->mlme.wmm_enabled) { + ieee80211_sta_wmm_params(wpa_s, elems.wme, + elems.wme_len); + } +} + + +static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + int tx_last_beacon, adhoc; +#if 0 /* FIX */ + struct ieee80211_mgmt *resp; +#endif + u8 *pos, *end; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + adhoc = ssid && ssid->mode == 1; + + if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED || + len < 24 + 2 || wpa_s->mlme.probe_resp == NULL) + return; + +#if 0 /* FIX */ + if (local->hw->tx_last_beacon) + tx_last_beacon = local->hw->tx_last_beacon(local->mdev); + else +#endif + tx_last_beacon = 1; + +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR + " BSSID=" MACSTR " (tx_last_beacon=%d)", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), + MAC2STR(mgmt->bssid), tx_last_beacon); +#endif /* IEEE80211_IBSS_DEBUG */ + + if (!tx_last_beacon) + return; + + if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + return; + + end = ((u8 *) mgmt) + len; + pos = mgmt->u.probe_req.variable; + if (pos[0] != WLAN_EID_SSID || + pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + if (pos[1] != 0 && + (pos[1] != wpa_s->mlme.ssid_len || + os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0)) + { + /* Ignore ProbeReq for foreign SSID */ + return; + } + +#if 0 /* FIX */ + /* Reply with ProbeResp */ + skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC); + if (skb == NULL) + return; + + resp = (struct ieee80211_mgmt *) skb->data; + os_memcpy(resp->da, mgmt->sa, ETH_ALEN); +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR, + MAC2STR(resp->da)); +#endif /* IEEE80211_IBSS_DEBUG */ + ieee80211_sta_tx(wpa_s, skb, 0, 1); +#endif +} + + +#ifdef CONFIG_IEEE80211R +static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + union wpa_event_data data; + u16 status; + u8 *sta_addr, *target_ap_addr; + + if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) { + wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame"); + return; + } + + /* + * Only FT Action Response is needed for now since reservation + * protocol is not supported. + */ + if (mgmt->u.action.u.ft_action_resp.action != 2) { + wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d", + mgmt->u.action.u.ft_action_resp.action); + return; + } + + status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code); + sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr; + target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr; + wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR + " TargetAP " MACSTR " Status Code %d", + MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); + if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR + " in FT Action Response", MAC2STR(sta_addr)); + return; + + } + + if (status) { + wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates " + "failure (status code %d)", status); + /* TODO: report error to FT code(?) */ + return; + } + + os_memset(&data, 0, sizeof(data)); + data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable; + data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable - + (u8 *) mgmt); + data.ft_ies.ft_action = 1; + os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN); + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); + /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed + * successfully */ + wpa_s->mlme.prev_bssid_set = 1; + wpa_s->mlme.auth_alg = WLAN_AUTH_FT; + os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); + os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN); + ieee80211_associate(wpa_s); +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_IEEE80211W + +/* MLME-SAQuery.response */ +static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + + mgmt = os_zalloc(sizeof(*mgmt)); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "SA Query action frame"); + return -1; + } + + len = 24; + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_SA_QUERY; + mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + len += 1 + sizeof(mgmt->u.action.u.sa_query_resp); + + res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len); + os_free(mgmt); + + return res; +} + + +static void ieee80211_rx_mgmt_sa_query_action( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) +{ + if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) { + wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame"); + return; + } + + if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) { + wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d", + mgmt->u.action.u.sa_query_req.action); + return; + } + + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown " + "source " MACSTR, MAC2STR(mgmt->sa)); + return; + } + + if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) { + wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during " + "association process"); + return; + } + + wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request"); + ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u. + sa_query_req.trans_id); +} + +#endif /* CONFIG_IEEE80211W */ + + +static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + wpa_printf(MSG_DEBUG, "MLME: received Action frame"); + + if (len < 25) + return; + + switch (mgmt->u.action.category) { +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status); + break; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status); + break; +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d", + mgmt->u.action.category); + break; + } +} + + +static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s, + const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + switch (WLAN_FC_GET_STYPE(fc)) { + case WLAN_FC_STYPE_PROBE_REQ: + ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_AUTH: + ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1); + break; + case WLAN_FC_STYPE_DEAUTH: + ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_DISASSOC: + ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_ACTION: + ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status); + break; + default: + wpa_printf(MSG_DEBUG, "MLME: received unknown management " + "frame - stype=%d", WLAN_FC_GET_STYPE(fc)); + break; + } +} + + +static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s, + const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) { + if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, + len, rx_status); + } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { + ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); + } + } +} + + +static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s) +{ + int active = 0; + +#if 0 /* FIX */ + list_for_each(ptr, &local->sta_list) { + sta = list_entry(ptr, struct sta_info, list); + if (sta->dev == dev && + time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, + jiffies)) { + active++; + break; + } + } +#endif + + return active; +} + + +static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + list_for_each_safe(ptr, n, &local->sta_list) { + sta = list_entry(ptr, struct sta_info, list); + if (time_after(jiffies, sta->last_rx + + IEEE80211_IBSS_INACTIVITY_LIMIT)) { + wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA " + MACSTR, MAC2STR(sta->addr)); + sta_info_free(local, sta, 1); + } + } +#endif +} + + +static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s) +{ + ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); + + ieee80211_sta_expire(wpa_s); + if (ieee80211_sta_active_ibss(wpa_s)) + return; + + wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for " + "other IBSS networks with same SSID (merge)"); + ieee80211_sta_req_scan(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); +} + + +static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + switch (wpa_s->mlme.state) { + case IEEE80211_DISABLED: + break; + case IEEE80211_AUTHENTICATE: + ieee80211_authenticate(wpa_s); + break; + case IEEE80211_ASSOCIATE: + ieee80211_associate(wpa_s); + break; + case IEEE80211_ASSOCIATED: + ieee80211_associated(wpa_s); + break; + case IEEE80211_IBSS_SEARCH: + ieee80211_sta_find_ibss(wpa_s); + break; + case IEEE80211_IBSS_JOINED: + ieee80211_sta_merge_ibss(wpa_s); + break; + default: + wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d", + wpa_s->mlme.state); + break; + } + + if (ieee80211_privacy_mismatch(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch " + "and mixed-cell disabled - disassociate"); + + ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED); + ieee80211_set_associated(wpa_s, 0); + } +} + + +static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (ssid && ssid->mode != 0) + return; + +#if 0 /* FIX */ + if (local->hw->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->hw->reset_tsf(local->mdev); + } +#endif + + wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */ + + + if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_OPEN) + wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; + else if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) + wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY; + else if (wpa_s->mlme.auth_algs & IEEE80211_AUTH_ALG_LEAP) + wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP; + else + wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; + wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d", + wpa_s->mlme.auth_alg); + wpa_s->mlme.auth_transaction = -1; + wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0; + ieee80211_authenticate(wpa_s); +} + + +static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + int m, c; + + for (m = 0; m < local->hw->num_modes; m++) { + struct ieee80211_hw_modes *mode = &local->hw->modes[m]; + if (mode->mode != local->conf.phymode) + continue; + for (c = 0; c < mode->num_channels; c++) { + struct ieee80211_channel *chan = &mode->channels[c]; + if (chan->flag & IEEE80211_CHAN_W_SCAN && + chan->chan == local->conf.channel) { + if (chan->flag & IEEE80211_CHAN_W_IBSS) + return 1; + break; + } + } + } +#endif + + return 0; +} + + +static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + int res = 0, rates, done = 0; + struct ieee80211_mgmt *mgmt; +#if 0 /* FIX */ + struct ieee80211_tx_control control; + struct ieee80211_rate *rate; + struct rate_control_extra extra; +#endif + u8 *pos, *buf; + size_t len; + + /* Remove possible STA entries from other IBSS networks. */ +#if 0 /* FIX */ + sta_info_flush(local, NULL); + + if (local->hw->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->hw->reset_tsf(local->mdev); + } +#endif + os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN); + +#if 0 /* FIX */ + local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; + + sdata->drop_unencrypted = bss->capability & + host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0; +#endif + +#if 0 /* FIX */ + os_memset(&rq, 0, sizeof(rq)); + rq.m = bss->freq * 100000; + rq.e = 1; + res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL); +#endif + + if (!ieee80211_ibss_allowed(wpa_s)) { +#if 0 /* FIX */ + wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d " + "(%d MHz)", local->conf.channel, + local->conf.freq); +#endif + return -1; + } + + /* Set beacon template based on scan results */ + buf = os_malloc(400); + len = 0; + do { + if (buf == NULL) + break; + + mgmt = (struct ieee80211_mgmt *) buf; + len += 24 + sizeof(mgmt->u.beacon); + os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_BEACON); + os_memset(mgmt->da, 0xff, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); +#if 0 /* FIX */ + mgmt->u.beacon.beacon_int = + host_to_le16(local->conf.beacon_int); +#endif + mgmt->u.beacon.capab_info = host_to_le16(bss->capability); + + pos = buf + len; + len += 2 + wpa_s->mlme.ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = wpa_s->mlme.ssid_len; + os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + + rates = bss->supp_rates_len; + if (rates > 8) + rates = 8; + pos = buf + len; + len += 2 + rates; + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + os_memcpy(pos, bss->supp_rates, rates); + + pos = buf + len; + len += 2 + 1; + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = bss->channel; + + pos = buf + len; + len += 2 + 2; + *pos++ = WLAN_EID_IBSS_PARAMS; + *pos++ = 2; + /* FIX: set ATIM window based on scan results */ + *pos++ = 0; + *pos++ = 0; + + if (bss->supp_rates_len > 8) { + rates = bss->supp_rates_len - 8; + pos = buf + len; + len += 2 + rates; + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = rates; + os_memcpy(pos, &bss->supp_rates[8], rates); + } + +#if 0 /* FIX */ + os_memset(&control, 0, sizeof(control)); + control.pkt_type = PKT_PROBE_RESP; + os_memset(&extra, 0, sizeof(extra)); + extra.endidx = local->num_curr_rates; + rate = rate_control_get_rate(wpa_s, skb, &extra); + if (rate == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX " + "rate for IBSS beacon"); + break; + } + control.tx_rate = (wpa_s->mlme.short_preamble && + (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rate->val2 : rate->val; + control.antenna_sel = local->conf.antenna_sel; + control.power_level = local->conf.power_level; + control.no_ack = 1; + control.retry_limit = 1; + control.rts_cts_duration = 0; +#endif + +#if 0 /* FIX */ + wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC); + if (wpa_s->mlme.probe_resp) { + mgmt = (struct ieee80211_mgmt *) + wpa_s->mlme.probe_resp->data; + mgmt->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + } else { + wpa_printf(MSG_DEBUG, "MLME: Could not allocate " + "ProbeResp template for IBSS"); + } + + if (local->hw->beacon_update && + local->hw->beacon_update(wpa_s, skb, &control) == 0) { + wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon " + "template based on scan results"); + skb = NULL; + } + + rates = 0; + for (i = 0; i < bss->supp_rates_len; i++) { + int rate = (bss->supp_rates[i] & 0x7f) * 5; + if (local->conf.phymode == MODE_ATHEROS_TURBO) + rate *= 2; + for (j = 0; j < local->num_curr_rates; j++) + if (local->curr_rates[j].rate == rate) + rates |= BIT(j); + } + wpa_s->mlme.supp_rates_bits = rates; +#endif + done = 1; + } while (0); + + os_free(buf); + if (!done) { + wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon " + "template"); + } + + wpa_s->mlme.state = IEEE80211_IBSS_JOINED; + ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); + + return res; +} + + +#if 0 /* FIX */ +static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + u8 bssid[ETH_ALEN], *pos; + int i; + +#if 0 + /* Easier testing, use fixed BSSID. */ + os_memset(bssid, 0xfe, ETH_ALEN); +#else + /* Generate random, not broadcast, locally administered BSSID. Mix in + * own MAC address to make sure that devices that do not have proper + * random number generator get different BSSID. */ + os_get_random(bssid, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + bssid[i] ^= wpa_s->own_addr[i]; + bssid[0] &= ~0x01; + bssid[0] |= 0x02; +#endif + + wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID " + MACSTR "", MAC2STR(bssid)); + + bss = ieee80211_bss_add(wpa_s, bssid); + if (bss == NULL) + return -ENOMEM; + +#if 0 /* FIX */ + if (local->conf.beacon_int == 0) + local->conf.beacon_int = 100; + bss->beacon_int = local->conf.beacon_int; + bss->hw_mode = local->conf.phymode; + bss->channel = local->conf.channel; + bss->freq = local->conf.freq; +#endif + os_get_time(&bss->last_update); + bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS); +#if 0 /* FIX */ + if (sdata->default_key) { + bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY); + } else + sdata->drop_unencrypted = 0; + bss->supp_rates_len = local->num_curr_rates; +#endif + pos = bss->supp_rates; +#if 0 /* FIX */ + for (i = 0; i < local->num_curr_rates; i++) { + int rate = local->curr_rates[i].rate; + if (local->conf.phymode == MODE_ATHEROS_TURBO) + rate /= 2; + *pos++ = (u8) (rate / 5); + } +#endif + + return ieee80211_sta_join_ibss(wpa_s, bss); +} +#endif + + +static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + int found = 0; + u8 bssid[ETH_ALEN]; + int active_ibss; + struct os_time now; + + if (wpa_s->mlme.ssid_len == 0) + return -EINVAL; + + active_ibss = ieee80211_sta_active_ibss(wpa_s); +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)", + active_ibss); +#endif /* IEEE80211_IBSS_DEBUG */ + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { + if (wpa_s->mlme.ssid_len != bss->ssid_len || + os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0 + || !(bss->capability & WLAN_CAPABILITY_IBSS)) + continue; +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found", + MAC2STR(bss->bssid)); +#endif /* IEEE80211_IBSS_DEBUG */ + os_memcpy(bssid, bss->bssid, ETH_ALEN); + found = 1; + if (active_ibss || + os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) + break; + } + +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current " + MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid)); +#endif /* IEEE80211_IBSS_DEBUG */ + if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 && + (bss = ieee80211_bss_get(wpa_s, bssid))) { + wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR + " based on configured SSID", + MAC2STR(bssid)); + return ieee80211_sta_join_ibss(wpa_s, bss); + } +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " did not try to join ibss"); +#endif /* IEEE80211_IBSS_DEBUG */ + + /* Selected IBSS not found in current scan results - try to scan */ + os_get_time(&now); +#if 0 /* FIX */ + if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(wpa_s)) { + ieee80211_reschedule_timer(wpa_s, + IEEE80211_IBSS_MERGE_INTERVAL); + } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed + + IEEE80211_SCAN_INTERVAL)) { + wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS " + "to join"); + return ieee80211_sta_req_scan(wpa_s->mlme.ssid, + wpa_s->mlme.ssid_len); + } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) { + int interval = IEEE80211_SCAN_INTERVAL; + + if (time_after(jiffies, wpa_s->mlme.ibss_join_req + + IEEE80211_IBSS_JOIN_TIMEOUT)) { + if (wpa_s->mlme.create_ibss && + ieee80211_ibss_allowed(wpa_s)) + return ieee80211_sta_create_ibss(wpa_s); + if (wpa_s->mlme.create_ibss) { + wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed " + "on the configured channel %d " + "(%d MHz)", + local->conf.channel, + local->conf.freq); + } + + /* No IBSS found - decrease scan interval and continue + * scanning. */ + interval = IEEE80211_SCAN_INTERVAL_SLOW; + } + + wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; + ieee80211_reschedule_timer(wpa_s, interval); + return 0; + } +#endif + + return 0; +} + + +int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, + size_t *len) +{ + os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + *len = wpa_s->mlme.ssid_len; + return 0; +} + + +int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_sta_bss *bss; + + wpa_s->mlme.bssid_set = 0; + wpa_s->mlme.freq = params->freq; + if (params->bssid) { + os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN); + if (!is_zero_ether_addr(params->bssid)) + wpa_s->mlme.bssid_set = 1; + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss) { + wpa_s->mlme.phymode = bss->hw_mode; + wpa_s->mlme.channel = bss->channel; + wpa_s->mlme.freq = bss->freq; + } + } + +#if 0 /* FIX */ + /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is + * not defined. */ + if (local->hw->conf_tx) { + struct ieee80211_tx_queue_params qparam; + int i; + + os_memset(&qparam, 0, sizeof(qparam)); + /* TODO: are these ok defaults for all hw_modes? */ + qparam.aifs = 2; + qparam.cw_min = + local->conf.phymode == MODE_IEEE80211B ? 31 : 15; + qparam.cw_max = 1023; + qparam.burst_time = 0; + for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) + { + local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0, + &qparam); + } + /* IBSS uses different parameters for Beacon sending */ + qparam.cw_min++; + qparam.cw_min *= 2; + qparam.cw_min--; + local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam); + } +#endif + + if (wpa_s->mlme.ssid_len != params->ssid_len || + os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0) + wpa_s->mlme.prev_bssid_set = 0; + os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len); + os_memset(wpa_s->mlme.ssid + params->ssid_len, 0, + MAX_SSID_LEN - params->ssid_len); + wpa_s->mlme.ssid_len = params->ssid_len; + wpa_s->mlme.ssid_set = 1; + + os_free(wpa_s->mlme.extra_ie); + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + wpa_s->mlme.extra_ie = NULL; + wpa_s->mlme.extra_ie_len = 0; + } else { + wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len); + if (wpa_s->mlme.extra_ie == NULL) { + wpa_s->mlme.extra_ie_len = 0; + return -1; + } + os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie, + params->wpa_ie_len); + wpa_s->mlme.extra_ie_len = params->wpa_ie_len; + } + + wpa_s->mlme.key_mgmt = params->key_mgmt_suite; + + ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, + wpa_s->mlme.channel, wpa_s->mlme.freq); + + if (params->mode == 1 && !wpa_s->mlme.bssid_set) { + os_get_time(&wpa_s->mlme.ibss_join_req); + wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; + return ieee80211_sta_find_ibss(wpa_s); + } + + if (wpa_s->mlme.bssid_set) + ieee80211_sta_new_auth(wpa_s); + + return 0; +} + + +static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel; + wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq; + wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode; +} + + +static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel; + wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq; + wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode; + if (wpa_s->mlme.freq == 0) + return 0; + return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, + wpa_s->mlme.channel, + wpa_s->mlme.freq); +} + + +static int ieee80211_active_scan(struct wpa_supplicant *wpa_s) +{ + size_t m; + int c; + + for (m = 0; m < wpa_s->mlme.num_modes; m++) { + struct wpa_hw_modes *mode = &wpa_s->mlme.modes[m]; + if ((int) mode->mode != (int) wpa_s->mlme.phymode) + continue; + for (c = 0; c < mode->num_channels; c++) { + struct wpa_channel_data *chan = &mode->channels[c]; + if (chan->flag & WPA_CHAN_W_SCAN && + chan->chan == wpa_s->mlme.channel) { + if (chan->flag & WPA_CHAN_W_ACTIVE_SCAN) + return 1; + break; + } + } + } + + return 0; +} + + +static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_hw_modes *mode; + struct wpa_channel_data *chan; + int skip = 0; + int timeout = 0; + struct wpa_ssid *ssid = wpa_s->current_ssid; + int adhoc; + + if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL) + return; + + adhoc = ssid && ssid->mode == 1; + + switch (wpa_s->mlme.scan_state) { + case SCAN_SET_CHANNEL: + mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]; + if (wpa_s->mlme.scan_hw_mode_idx >= + (int) wpa_s->mlme.num_modes || + (wpa_s->mlme.scan_hw_mode_idx + 1 == + (int) wpa_s->mlme.num_modes + && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) { + if (ieee80211_sta_restore_oper_chan(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: failed to " + "restore operational channel after " + "scan"); + } + wpa_printf(MSG_DEBUG, "MLME: scan completed"); + wpa_s->mlme.sta_scanning = 0; + os_get_time(&wpa_s->mlme.last_scan_completed); + wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); + if (adhoc) { + if (!wpa_s->mlme.bssid_set || + (wpa_s->mlme.state == + IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(wpa_s))) + ieee80211_sta_find_ibss(wpa_s); + } + return; + } + skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode)); + chan = &mode->channels[wpa_s->mlme.scan_channel_idx]; + if (!(chan->flag & WPA_CHAN_W_SCAN) || + (adhoc && !(chan->flag & WPA_CHAN_W_IBSS)) || + (wpa_s->mlme.hw_modes & (1 << WPA_MODE_IEEE80211G) && + mode->mode == WPA_MODE_IEEE80211B && + wpa_s->mlme.scan_skip_11b)) + skip = 1; + + if (!skip) { + wpa_printf(MSG_MSGDUMP, + "MLME: scan channel %d (%d MHz)", + chan->chan, chan->freq); + + wpa_s->mlme.channel = chan->chan; + wpa_s->mlme.freq = chan->freq; + wpa_s->mlme.phymode = mode->mode; + if (ieee80211_sta_set_channel(wpa_s, mode->mode, + chan->chan, chan->freq)) + { + wpa_printf(MSG_DEBUG, "MLME: failed to set " + "channel %d (%d MHz) for scan", + chan->chan, chan->freq); + skip = 1; + } + } + + wpa_s->mlme.scan_channel_idx++; + if (wpa_s->mlme.scan_channel_idx >= + wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]. + num_channels) { + wpa_s->mlme.scan_hw_mode_idx++; + wpa_s->mlme.scan_channel_idx = 0; + } + + if (skip) { + timeout = 0; + break; + } + + timeout = IEEE80211_PROBE_DELAY; + wpa_s->mlme.scan_state = SCAN_SEND_PROBE; + break; + case SCAN_SEND_PROBE: + if (ieee80211_active_scan(wpa_s)) { + ieee80211_send_probe_req(wpa_s, NULL, + wpa_s->mlme.scan_ssid, + wpa_s->mlme.scan_ssid_len); + timeout = IEEE80211_CHANNEL_TIME; + } else { + timeout = IEEE80211_PASSIVE_CHANNEL_TIME; + } + wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; + break; + } + + eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000), + ieee80211_sta_scan_timer, wpa_s, NULL); +} + + +int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len) +{ + if (ssid_len > MAX_SSID_LEN) + return -1; + + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) + * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS + * BSSID: MACAddress + * SSID + * ScanType: ACTIVE, PASSIVE + * ProbeDelay: delay (in microseconds) to be used prior to transmitting + * a Probe frame during active scanning + * ChannelList + * MinChannelTime (>= ProbeDelay), in TU + * MaxChannelTime: (>= MinChannelTime), in TU + */ + + /* MLME-SCAN.confirm + * BSSDescriptionSet + * ResultCode: SUCCESS, INVALID_PARAMETERS + */ + + /* TODO: if assoc, move to power save mode for the duration of the + * scan */ + + if (wpa_s->mlme.sta_scanning) + return -1; + + wpa_printf(MSG_DEBUG, "MLME: starting scan"); + + ieee80211_sta_save_oper_chan(wpa_s); + + wpa_s->mlme.sta_scanning = 1; + /* TODO: stop TX queue? */ + + if (ssid) { + wpa_s->mlme.scan_ssid_len = ssid_len; + os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len); + } else + wpa_s->mlme.scan_ssid_len = 0; + wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not + * supported */ + wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; + wpa_s->mlme.scan_hw_mode_idx = 0; + wpa_s->mlme.scan_channel_idx = 0; + eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL); + + return 0; +} + + +struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) +{ + size_t ap_num = 0; + struct wpa_scan_results *res; + struct wpa_scan_res *r; + struct ieee80211_sta_bss *bss; + + res = os_zalloc(sizeof(*res)); + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) + ap_num++; + res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { + r = os_zalloc(sizeof(*r) + bss->ie_len); + if (r == NULL) + break; + os_memcpy(r->bssid, bss->bssid, ETH_ALEN); + r->freq = bss->freq; + r->beacon_int = bss->beacon_int; + r->caps = bss->capability; + r->level = bss->rssi; + r->tsf = bss->timestamp; + if (bss->ie) { + r->ie_len = bss->ie_len; + os_memcpy(r + 1, bss->ie, bss->ie_len); + } + + res->res[res->num++] = r; + } + + return res; +} + + +#if 0 /* FIX */ +struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s, + struct sk_buff *skb, u8 *bssid, + u8 *addr) +{ + struct ieee80211_local *local = dev->priv; + struct list_head *ptr; + struct sta_info *sta; + struct wpa_supplicant *sta_dev = NULL; + + /* TODO: Could consider removing the least recently used entry and + * allow new one to be added. */ + if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { + if (net_ratelimit()) { + wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS " + "STA entry " MACSTR, MAC2STR(addr)); + } + return NULL; + } + + spin_lock_bh(&local->sub_if_lock); + list_for_each(ptr, &local->sub_if_list) { + sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); + if (sdata->type == IEEE80211_SUB_IF_TYPE_STA && + os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { + sta_dev = sdata->dev; + break; + } + } + spin_unlock_bh(&local->sub_if_lock); + + if (sta_dev == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR + " (dev=%s)", MAC2STR(addr), sta_dev->name); + + sta = sta_info_add(wpa_s, addr); + if (sta == NULL) { + return NULL; + } + + sta->dev = sta_dev; + sta->supp_rates = wpa_s->mlme.supp_rates_bits; + + rate_control_rate_init(local, sta); + + return sta; /* caller will call sta_info_release() */ +} +#endif + + +int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason) +{ + wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason); + + ieee80211_send_deauth(wpa_s, reason); + ieee80211_set_associated(wpa_s, 0); + return 0; +} + + +int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason) +{ + wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason); + + if (!wpa_s->mlme.associated) + return -1; + + ieee80211_send_disassoc(wpa_s, reason); + ieee80211_set_associated(wpa_s, 0); + return 0; +} + + +void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + const u8 *pos; + + /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */ + + if (wpa_s->mlme.sta_scanning) { + ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status); + return; + } + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) + ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status); + else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != + WLAN_FC_FROMDS) + return; + /* mgmt->sa is actually BSSID for FromDS data frames */ + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + /* Skip IEEE 802.11 and LLC headers */ + pos = buf + 24 + 6; + if (WPA_GET_BE16(pos) != ETH_P_EAPOL) + return; + pos += 2; + /* mgmt->bssid is actually BSSID for SA data frames */ + wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid, + pos, buf + len - pos); + } +} + + +void ieee80211_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +int ieee80211_sta_init(struct wpa_supplicant *wpa_s) +{ + u16 num_modes, flags; + + wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, + &flags); + if (wpa_s->mlme.modes == NULL) { + wpa_printf(MSG_ERROR, "MLME: Failed to read supported " + "channels and rates from the driver"); + return -1; + } + + wpa_s->mlme.num_modes = num_modes; + + wpa_s->mlme.hw_modes = 1 << WPA_MODE_IEEE80211A; + wpa_s->mlme.hw_modes |= 1 << WPA_MODE_IEEE80211B; + wpa_s->mlme.hw_modes |= 1 << WPA_MODE_IEEE80211G; + + return 0; +} + + +void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); + eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL); + os_free(wpa_s->mlme.extra_ie); + wpa_s->mlme.extra_ie = NULL; + os_free(wpa_s->mlme.extra_probe_ie); + wpa_s->mlme.extra_probe_ie = NULL; + os_free(wpa_s->mlme.assocreq_ies); + wpa_s->mlme.assocreq_ies = NULL; + os_free(wpa_s->mlme.assocresp_ies); + wpa_s->mlme.assocresp_ies = NULL; + ieee80211_bss_list_deinit(wpa_s); + ieee80211_sta_free_hw_features(wpa_s->mlme.modes, + wpa_s->mlme.num_modes); +#ifdef CONFIG_IEEE80211R + os_free(wpa_s->mlme.ft_ies); + wpa_s->mlme.ft_ies = NULL; + wpa_s->mlme.ft_ies_len = 0; +#endif /* CONFIG_IEEE80211R */ +} + + +#ifdef CONFIG_IEEE80211R + +int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (md == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain"); + os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN); + } else { + wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR, + MAC2STR(md)); + os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN); + } + + wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len); + os_free(wpa_s->mlme.ft_ies); + wpa_s->mlme.ft_ies = os_malloc(ies_len); + if (wpa_s->mlme.ft_ies == NULL) + return -1; + os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len); + wpa_s->mlme.ft_ies_len = ies_len; + + return 0; +} + + +int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + int res; + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + buf = os_zalloc(sizeof(*mgmt) + ies_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "FT action frame"); + return -1; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_FT; + mgmt->u.action.u.ft_action_req.action = action; + os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr, + ETH_ALEN); + os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap, + ETH_ALEN); + os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len); + len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len; + + wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d " + "Target AP=" MACSTR " body_len=%lu", + action, MAC2STR(target_ap), (unsigned long) ies_len); + + res = ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); + + return res; +} + +#endif /* CONFIG_IEEE80211R */ + + +int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len) +{ + os_free(wpa_s->mlme.extra_probe_ie); + wpa_s->mlme.extra_probe_ie = NULL; + wpa_s->mlme.extra_probe_ie_len = 0; + + if (ies == NULL) + return 0; + + wpa_s->mlme.extra_probe_ie = os_malloc(ies_len); + if (wpa_s->mlme.extra_probe_ie == NULL) + return -1; + + os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len); + wpa_s->mlme.extra_probe_ie_len = ies_len; + + return 0; +} diff --git a/wpa_supplicant/mlme.h b/wpa_supplicant/mlme.h new file mode 100644 index 0000000000000..cc58a5b40868b --- /dev/null +++ b/wpa_supplicant/mlme.h @@ -0,0 +1,132 @@ +/* + * WPA Supplicant - Client mode MLME + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MLME_H +#define MLME_H + +struct wpa_supplicant; + +#ifdef CONFIG_CLIENT_MLME + +int ieee80211_sta_init(struct wpa_supplicant *wpa_s); +void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s); +int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len); +int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason); +int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason); +int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params); +int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, + size_t *len); +void ieee80211_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features); +void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status); +struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s); +int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len); +int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len); +int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len); + +#else /* CONFIG_CLIENT_MLME */ + +static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len) +{ + return -1; +} + +static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, + u16 reason) +{ + return -1; +} + +static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, + u16 reason) +{ + return -1; +} + +static inline int +ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + return -1; +} + +static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, + u8 *ssid, size_t *len) +{ + return -1; +} + +static inline void +ieee80211_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features) +{ +} + +static inline void +ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ +} + +static inline struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) +{ + return NULL; +} + +static inline int +ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + return -1; +} + +static inline int +ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + return -1; +} + +static inline int +ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len) +{ + return -1; +} + +#endif /* CONFIG_CLIENT_MLME */ + +#endif /* MLME_H */ diff --git a/wpa_supplicant/nmake.mak b/wpa_supplicant/nmake.mak new file mode 100644 index 0000000000000..5e39c117d3963 --- /dev/null +++ b/wpa_supplicant/nmake.mak @@ -0,0 +1,228 @@ +# Makefile for Microsoft nmake to build wpa_supplicant + +# This can be run in Visual Studio 2005 Command Prompt + +# Note: Make sure that cl.exe is configured to include Platform SDK +# include and lib directories (vsvars32.bat) + +all: wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe wpasvc.exe win_if_list.exe + +# Root directory for WinPcap developer's pack +# (http://www.winpcap.org/install/bin/WpdPack_3_1.zip) +WINPCAPDIR=C:\dev\WpdPack + +# Root directory for OpenSSL +# (http://www.openssl.org/source/openssl-0.9.8a.tar.gz) +# Build and installed following instructions in INSTALL.W32 +# Note: If EAP-FAST is included in the build, OpenSSL needs to be patched to +# support it (openssl-tls-extensions.patch) +# Alternatively, see README-Windows.txt for information about binary +# installation package for OpenSSL. +OPENSSLDIR=C:\dev\openssl + +CC = cl +OBJDIR = objs + +CFLAGS = /DCONFIG_NATIVE_WINDOWS +CFLAGS = $(CFLAGS) /DCONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS = $(CFLAGS) /DCONFIG_ANSI_C_EXTRA +CFLAGS = $(CFLAGS) /DCONFIG_WINPCAP +CFLAGS = $(CFLAGS) /DIEEE8021X_EAPOL +CFLAGS = $(CFLAGS) /DEAP_TLS_FUNCS +CFLAGS = $(CFLAGS) /DPKCS12_FUNCS +CFLAGS = $(CFLAGS) /DEAP_MD5 +CFLAGS = $(CFLAGS) /DEAP_TLS +CFLAGS = $(CFLAGS) /DEAP_MSCHAPv2 +CFLAGS = $(CFLAGS) /DEAP_PEAP +CFLAGS = $(CFLAGS) /DEAP_TTLS +CFLAGS = $(CFLAGS) /DEAP_GTC +CFLAGS = $(CFLAGS) /DEAP_OTP +CFLAGS = $(CFLAGS) /DEAP_SIM +CFLAGS = $(CFLAGS) /DEAP_LEAP +CFLAGS = $(CFLAGS) /DEAP_PSK +CFLAGS = $(CFLAGS) /DEAP_AKA +#CFLAGS = $(CFLAGS) /DEAP_FAST +CFLAGS = $(CFLAGS) /DEAP_PAX +CFLAGS = $(CFLAGS) /DEAP_TNC +CFLAGS = $(CFLAGS) /DPCSC_FUNCS +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE_NAMED_PIPE +CFLAGS = $(CFLAGS) /DCONFIG_DRIVER_NDIS +CFLAGS = $(CFLAGS) /I..\src /I..\src\utils /I..\src\common /I..\src\crypto +CFLAGS = $(CFLAGS) /I..\src\rsn_supp /I..\src\eapol_supp /I. +CFLAGS = $(CFLAGS) /DWIN32 +CFLAGS = $(CFLAGS) /Fo$(OBJDIR)\\ /c +CFLAGS = $(CFLAGS) /W3 + +#CFLAGS = $(CFLAGS) /WX + +# VS 2005 complains about lot of deprecated string functions; let's ignore them +# at least for now since snprintf and strncpy can be used in a safe way +CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE + +OBJS = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\eloop_win.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\rc4.obj \ + $(OBJDIR)\aes_wrap.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\wpa_debug.obj \ + $(OBJDIR)\wpabuf.obj \ + $(OBJDIR)\wpa_supplicant.obj \ + $(OBJDIR)\wpa.obj \ + $(OBJDIR)\wpa_common.obj \ + $(OBJDIR)\wpa_ie.obj \ + $(OBJDIR)\preauth.obj \ + $(OBJDIR)\pmksa_cache.obj \ + $(OBJDIR)\eapol_supp_sm.obj \ + $(OBJDIR)\eap.obj \ + $(OBJDIR)\eap_common.obj \ + $(OBJDIR)\chap.obj \ + $(OBJDIR)\eap_methods.obj \ + $(OBJDIR)\eap_md5.obj \ + $(OBJDIR)\eap_tls.obj \ + $(OBJDIR)\eap_tls_common.obj \ + $(OBJDIR)\eap_mschapv2.obj \ + $(OBJDIR)\mschapv2.obj \ + $(OBJDIR)\eap_peap.obj \ + $(OBJDIR)\eap_peap_common.obj \ + $(OBJDIR)\eap_ttls.obj \ + $(OBJDIR)\eap_gtc.obj \ + $(OBJDIR)\eap_otp.obj \ + $(OBJDIR)\eap_leap.obj \ + $(OBJDIR)\eap_sim.obj \ + $(OBJDIR)\eap_sim_common.obj \ + $(OBJDIR)\eap_aka.obj \ + $(OBJDIR)\eap_pax.obj \ + $(OBJDIR)\eap_pax_common.obj \ + $(OBJDIR)\eap_psk.obj \ + $(OBJDIR)\eap_psk_common.obj \ + $(OBJDIR)\eap_tnc.obj \ + $(OBJDIR)\tncc.obj \ + $(OBJDIR)\base64.obj \ + $(OBJDIR)\ctrl_iface.obj \ + $(OBJDIR)\ctrl_iface_named_pipe.obj \ + $(OBJDIR)\driver_ndis.obj \ + $(OBJDIR)\driver_ndis_.obj \ + $(OBJDIR)\scan_helpers.obj \ + $(OBJDIR)\events.obj \ + $(OBJDIR)\blacklist.obj \ + $(OBJDIR)\scan.obj \ + $(OBJDIR)\wpas_glue.obj \ + $(OBJDIR)\config.obj \ + $(OBJDIR)\l2_packet_winpcap.obj \ + $(OBJDIR)\tls_openssl.obj \ + $(OBJDIR)\ms_funcs.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\pcsc_funcs.obj \ + $(OBJDIR)\ndis_events.obj + +# OBJS = $(OBJS) $(OBJDIR)\eap_fast.obj + +OBJS_t = $(OBJS) \ + $(OBJDIR)\eapol_test.obj \ + $(OBJDIR)\radius.obj \ + $(OBJDIR)\radius_client.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS_t2 = $(OBJS) \ + $(OBJDIR)\preauth_test.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS2 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_file.obj \ + $(OBJS2) $(OBJDIR)\main.obj + +OBJS3 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_winreg.obj \ + $(OBJS3) $(OBJDIR)\main_winsvc.obj + +OBJS_c = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\wpa_cli.obj \ + $(OBJDIR)\wpa_ctrl.obj \ + $(OBJDIR)\common.obj + +OBJS_p = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\wpa_passphrase.obj + +LIBS = wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \ + ws2_32.lib Advapi32.lib Crypt32.lib Winscard.lib \ + Packet.lib wpcap.lib \ + libeay32.lib ssleay32.lib +# If using Win32 OpenSSL binary installation from Shining Light Productions, +# replace the last line with this for dynamic libraries +# libeay32MT.lib ssleay32MT.lib +# and this for static libraries +# libeay32MT.lib ssleay32MT.lib Gdi32.lib User32.lib + +CFLAGS = $(CFLAGS) /I"$(WINPCAPDIR)/Include" /I"$(OPENSSLDIR)\include" +LFLAGS = /libpath:"$(WINPCAPDIR)\Lib" /libpath:"$(OPENSSLDIR)\lib" + +wpa_supplicant.exe: $(OBJDIR) $(OBJS) $(OBJS2) + link.exe /out:wpa_supplicant.exe $(LFLAGS) $(OBJS) $(OBJS2) $(LIBS) + +wpasvc.exe: $(OBJDIR) $(OBJS) $(OBJS3) + link.exe /out:wpasvc.exe $(LFLAGS) $(OBJS) $(OBJS3) $(LIBS) + +wpa_cli.exe: $(OBJDIR) $(OBJS_c) + link.exe /out:wpa_cli.exe $(LFLAGS) $(OBJS_c) $(LIBS) + +wpa_passphrase.exe: $(OBJDIR) $(OBJS_p) + link.exe /out:wpa_passphrase.exe $(LFLAGS) $(OBJS_p) $(LIBS) + +eapol_test.exe: $(OBJDIR) $(OBJS_t) + link.exe /out:eapol_test.exe $(LFLAGS) $(OBJS_t) $(LIBS) + +preauth_test.exe: $(OBJDIR) $(OBJS_t2) + link.exe /out:preauth_test.exe $(LFLAGS) $(OBJS_t2) $(LIBS) + +win_if_list.exe: $(OBJDIR) $(OBJDIR)\win_if_list.obj + link.exe /out:win_if_list.exe $(LFLAGS) $(OBJDIR)\win_if_list.obj $(LIBS) + + +{..\src\utils}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\rsn_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eapol_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\crypto}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_peer}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\drivers}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\l2_packet}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.cpp{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +$(OBJDIR): + if not exist "$(OBJDIR)" mkdir "$(OBJDIR)" + +clean: + erase $(OBJDIR)\*.obj wpa_supplicant.exe diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c new file mode 100644 index 0000000000000..86307a883cf79 --- /dev/null +++ b/wpa_supplicant/preauth_test.c @@ -0,0 +1,375 @@ +/* + * WPA Supplicant - test code for pre-authentication + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include "includes.h" +#include <assert.h> + +#include "common.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eloop.h" +#include "wpa.h" +#include "eap_peer/eap.h" +#include "wpa_supplicant_i.h" +#include "l2_packet/l2_packet.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" +#include "preauth.h" +#include "pmksa_cache.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + +struct wpa_driver_ops *wpa_supplicant_drivers[] = { NULL }; + + +struct preauth_test_data { + int auth_timed_out; +}; + + +static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) +{ + wpa_supplicant_disassociate(wpa_s, reason_code); +} + + +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +{ + wpa_supplicant_deauthenticate(wpa_s, reason_code); +} + + +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos); +} + + +static void _wpa_supplicant_set_state(void *ctx, wpa_states state) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->wpa_state = state; +} + + +static wpa_states _wpa_supplicant_get_state(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_s->wpa_state; +} + + +static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto, + const u8 *buf, size_t len) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static void * wpa_supplicant_get_network_ctx(void *wpa_s) +{ + return wpa_supplicant_get_ssid(wpa_s); +} + + +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s) +{ + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static int wpa_supplicant_get_beacon_ie(void *wpa_s) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_set_key(void *wpa_s, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, + int protection_type, + int key_type) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_add_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_remove_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static void wpa_supplicant_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); +} + + +static const struct wpa_config_blob * +wpa_supplicant_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} + + +static void test_eapol_clean(struct wpa_supplicant *wpa_s) +{ + rsn_preauth_deinit(wpa_s->wpa); + pmksa_candidate_free(wpa_s->wpa); + wpa_sm_deinit(wpa_s->wpa); + scard_deinit(wpa_s->scard); + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + wpa_config_free(wpa_s->conf); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct preauth_test_data *p = eloop_ctx; + printf("EAPOL test timed out\n"); + p->auth_timed_out = 1; + eloop_terminate(); +} + + +static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (!rsn_preauth_in_progress(wpa_s->wpa)) + eloop_terminate(); + else { + eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx, + timeout_ctx); + } +} + + +static struct wpa_driver_ops dummy_driver; + + +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) +{ + struct l2_packet_data *l2; + struct wpa_sm_ctx *ctx; + + os_memset(&dummy_driver, 0, sizeof(dummy_driver)); + wpa_s->driver = &dummy_driver; + + ctx = os_zalloc(sizeof(*ctx)); + assert(ctx != NULL); + + ctx->ctx = wpa_s; + ctx->set_state = _wpa_supplicant_set_state; + ctx->get_state = _wpa_supplicant_get_state; + ctx->deauthenticate = _wpa_supplicant_deauthenticate; + ctx->disassociate = _wpa_supplicant_disassociate; + ctx->set_key = wpa_supplicant_set_key; + ctx->get_network_ctx = wpa_supplicant_get_network_ctx; + ctx->get_bssid = wpa_supplicant_get_bssid; + ctx->ether_send = wpa_ether_send; + ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie; + ctx->alloc_eapol = _wpa_alloc_eapol; + ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout; + ctx->add_pmkid = wpa_supplicant_add_pmkid; + ctx->remove_pmkid = wpa_supplicant_remove_pmkid; + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; + ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection; + + wpa_s->wpa = wpa_sm_init(ctx); + assert(wpa_s->wpa != NULL); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN); + + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL); + + l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL, + NULL, 0); + assert(l2 != NULL); + if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) { + wpa_printf(MSG_WARNING, "Failed to get own L2 address\n"); + exit(-1); + } + l2_packet_deinit(l2); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); +} + + +static void eapol_test_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int ret = 1; + u8 bssid[ETH_ALEN]; + struct preauth_test_data preauth_test; + + if (os_program_init()) + return -1; + + os_memset(&preauth_test, 0, sizeof(preauth_test)); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (argc != 4) { + printf("usage: preauth_test <conf> <target MAC address> " + "<ifname>\n"); + return -1; + } + + if (hwaddr_aton(argv[2], bssid)) { + printf("Failed to parse target address '%s'.\n", argv[2]); + return -1; + } + + if (eap_peer_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + if (eloop_init(&wpa_s)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = wpa_config_read(argv[1]); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", argv[1]); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + wpa_init_conf(&wpa_s, argv[3]); + wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); + if (wpa_s.ctrl_iface == NULL) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another preauth_test process already " + "running or the file was\n" + "left by an unclean termination of preauth_test in " + "which case you will need\n" + "to manually remove this file before starting " + "preauth_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; + + if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap)) + return -1; + + eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL); + eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL); + eloop_register_signal_terminate(eapol_test_terminate, NULL); + eloop_register_signal_reconfig(eapol_test_terminate, NULL); + eloop_run(); + + if (preauth_test.auth_timed_out) + ret = -2; + else { + ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0) + ? 0 : -3; + } + + test_eapol_clean(&wpa_s); + + eap_peer_unregister_methods(); + + eloop_destroy(); + + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c new file mode 100644 index 0000000000000..67e2282efd728 --- /dev/null +++ b/wpa_supplicant/scan.c @@ -0,0 +1,263 @@ +/* + * WPA Supplicant - Scanning + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "mlme.h" +#include "wps_supplicant.h" + + +static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + union wpa_event_data data; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) + return; + + if (wpa_s->current_ssid == NULL) + wpa_s->current_ssid = ssid; + wpa_supplicant_initiate_eapol(wpa_s); + wpa_printf(MSG_DEBUG, "Already associated with a configured network - " + "generating associated event"); + os_memset(&data, 0, sizeof(data)); + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); +} + + +#ifdef CONFIG_WPS +static int wpas_wps_in_use(struct wpa_config *conf, + enum wps_request_type *req_type) +{ + struct wpa_ssid *ssid; + int wps = 0; + + for (ssid = conf->ssid; ssid; ssid = ssid->next) { + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + continue; + + wps = 1; + *req_type = wpas_wps_get_req_type(ssid); + if (!ssid->eap.phase1) + continue; + + if (os_strstr(ssid->eap.phase1, "pbc=1")) + return 2; + } + + return wps; +} +#endif /* CONFIG_WPS */ + +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + int enabled, scan_req = 0, ret; + struct wpabuf *wps_ie = NULL; + const u8 *extra_ie = NULL; + size_t extra_ie_len = 0; + int wps = 0; +#ifdef CONFIG_WPS + enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; +#endif /* CONFIG_WPS */ + + if (wpa_s->disconnected && !wpa_s->scan_req) + return; + + enabled = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!ssid->disabled) { + enabled++; + break; + } + ssid = ssid->next; + } + if (!enabled && !wpa_s->scan_req) { + wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + return; + } + scan_req = wpa_s->scan_req; + wpa_s->scan_req = 0; + + if (wpa_s->conf->ap_scan != 0 && + wpa_s->driver && IS_WIRED(wpa_s->driver)) { + wpa_printf(MSG_DEBUG, "Using wired authentication - " + "overriding ap_scan configuration"); + wpa_s->conf->ap_scan = 0; + } + + if (wpa_s->conf->ap_scan == 0) { + wpa_supplicant_gen_assoc_event(wpa_s); + return; + } + + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_INACTIVE) + wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) { + while (ssid) { + if (ssid == wpa_s->prev_scan_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + while (ssid) { + if (!ssid->disabled && + (ssid->scan_ssid || wpa_s->conf->ap_scan == 2)) + break; + ssid = ssid->next; + } + + if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { + /* + * ap_scan=2 mode - try to associate with each SSID instead of + * scanning for each scan_ssid=1 network. + */ + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached " + "end of scan list - go back to beginning"); + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return; + } + if (ssid->next) { + /* Continue from the next SSID on the next attempt. */ + wpa_s->prev_scan_ssid = ssid; + } else { + /* Start from the beginning of the SSID list. */ + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + } + wpa_supplicant_associate(wpa_s, NULL, ssid); + return; + } + + wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)", + ssid ? "specific": "broadcast"); + if (ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", + ssid->ssid, ssid->ssid_len); + wpa_s->prev_scan_ssid = ssid; + } else + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + +#ifdef CONFIG_WPS + wps = wpas_wps_in_use(wpa_s->conf, &req_type); +#endif /* CONFIG_WPS */ + + if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 && + !wpa_s->use_client_mlme && wps != 2) { + wpa_s->scan_res_tried++; + wpa_s->scan_req = scan_req; + wpa_printf(MSG_DEBUG, "Trying to get current scan results " + "first without requesting a new scan to speed up " + "initial association"); + wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); + return; + } + +#ifdef CONFIG_WPS + if (wps) { + wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, + wpa_s->wps->uuid, req_type); + if (wps_ie) { + extra_ie = wpabuf_head(wps_ie); + extra_ie_len = wpabuf_len(wps_ie); + } + } +#endif /* CONFIG_WPS */ + + if (wpa_s->use_client_mlme) { + ieee80211_sta_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); + ret = ieee80211_sta_req_scan(wpa_s, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + } else { + wpa_drv_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); + ret = wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + } + + wpabuf_free(wps_ie); + + if (ret) { + wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_supplicant_req_scan(wpa_s, 10, 0); + } else + wpa_s->scan_runs++; +} + + +/** + * wpa_supplicant_req_scan - Schedule a scan for neighboring access points + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to scan + * @usec: Number of microseconds after which to scan + * + * This function is used to schedule a scan for neighboring access points after + * the specified time. + */ +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ + /* If there's at least one network that should be specifically scanned + * then don't cancel the scan and reschedule. Some drivers do + * background scanning which generates frequent scan results, and that + * causes the specific SSID scan to get continually pushed back and + * never happen, which causes hidden APs to never get probe-scanned. + */ + if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && + wpa_s->conf->ap_scan == 1) { + struct wpa_ssid *ssid = wpa_s->conf->ssid; + + while (ssid) { + if (!ssid->disabled && ssid->scan_ssid) + break; + ssid = ssid->next; + } + if (ssid) { + wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " + "ensure that specific SSID scans occur"); + return; + } + } + + wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", + sec, usec); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); +} + + +/** + * wpa_supplicant_cancel_scan - Cancel a scheduled scan request + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel a scan request scheduled with + * wpa_supplicant_req_scan(). + */ +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); +} diff --git a/wpa_supplicant/symbian/README.symbian b/wpa_supplicant/symbian/README.symbian new file mode 100644 index 0000000000000..9d3b811f0fc0e --- /dev/null +++ b/wpa_supplicant/symbian/README.symbian @@ -0,0 +1,24 @@ +wpa_supplicant for Symbian +========================== + +Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> and +contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + +This directory includes project files for testing experimental Symbian +(e.g., Nokia S60 3rd Ed) builds. The Symbian port is not really +complete or expected to work, but these files can be used to verify +that the build itself can be completed successfully. + +These files have been successfully tested with Nokia S60 3rd Edition +MR SDK. + +Build files can be created and a phone release build can be run with +following commands: + +bldmake bldfiles +abld build gcce urel diff --git a/wpa_supplicant/symbian/bld.inf b/wpa_supplicant/symbian/bld.inf new file mode 100644 index 0000000000000..a1fc582c52b5f --- /dev/null +++ b/wpa_supplicant/symbian/bld.inf @@ -0,0 +1,8 @@ +PRJ_PLATFORMS +WINSCW GCCE + +PRJ_EXPORTS + +PRJ_MMPFILES + +wpa_supplicant.mmp diff --git a/wpa_supplicant/symbian/wpa_supplicant.mmp b/wpa_supplicant/symbian/wpa_supplicant.mmp new file mode 100644 index 0000000000000..fad9626b83880 --- /dev/null +++ b/wpa_supplicant/symbian/wpa_supplicant.mmp @@ -0,0 +1,38 @@ +TARGET wpa_supplicant.exe +UID 0x0 0x0 +VENDORID 0 +TARGETTYPE exe + +SYSTEMINCLUDE \epoc32\include \epoc32\include\variant \epoc32\include\ecom \epoc32\include\libc + +USERINCLUDE .. ..\..\src ..\..\src\utils ..\..\src\common ..\..\src\crypto ..\..\src\rsn_supp + +SOURCEPATH .. +SOURCE main_symbian.cpp +SOURCE config.c config_file.c +SOURCE eapol_sm.c +SOURCE wpa_supplicant.c events.c +SOURCEPATH ..\..\src\rsn_supp +SOURCE wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c +SOURCEPATH ..\..\src\drivers +SOURCE drivers.c +SOURCEPATH ..\..\src\common +SOURCE wpa_common.c +SOURCEPATH ..\..\src\utils +SOURCE os_none.c common.c wpa_debug.c eloop_none.c base64.c +SOURCEPATH ..\..\src\crypto +SOURCE sha1.c md5.c rc4.c md4.c des.c aes_wrap.c aes.c ms_funcs.c +SOURCE tls_internal.c crypto_internal.c +SOURCEPATH ..\..\src\tls +SOURCE asn1.c bignum.c rsa.c x509v3.c tlsv1_client.c tlsv1_common.c +SOURCEPATH ..\..\src\l2_packet +SOURCE l2_packet_none.c +SOURCEPATH ..\..\src\eap_peer +SOURCE eap.c eap_methods.c +SOURCE eap_md5.c eap_tls.c eap_mschapv2.c eap_peap.c eap_gtc.c +SOURCE eap_ttls.c eap_otp.c eap_leap.c eap_tls_common.c eap_tlv.c +SOURCE eap_fast.c eap_fast_pac.c +SOURCEPATH ..\..\src\eap_common +SOURCE eap_common.c + +LIBRARY euser.lib estlib.lib diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c new file mode 100644 index 0000000000000..3bfbed577d8c0 --- /dev/null +++ b/wpa_supplicant/tests/link_test.c @@ -0,0 +1,83 @@ +/* + * Dummy functions to allow link_test to be linked. The need for these + * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to + * be built outside hostapd. + */ + +#include "includes.h" + +#include "common.h" + + +struct hostapd_data; +struct sta_info; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; +struct hostapd_eap_user; +struct hostapd_bss_config; +struct hostapd_vlan; + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + return NULL; +} + + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + return 0; +} + + +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ +} + + +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid) +{ + return 0; +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ +} + + +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) +{ +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ +} + + +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + return NULL; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + return NULL; +} diff --git a/wpa_supplicant/tests/test_aes.c b/wpa_supplicant/tests/test_aes.c new file mode 100644 index 0000000000000..38a9cf58d4309 --- /dev/null +++ b/wpa_supplicant/tests/test_aes.c @@ -0,0 +1,307 @@ +/* + * Test program for AES + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes_wrap.h" + +#define BLOCK_SIZE 16 + +static void test_aes_perf(void) +{ +#if 0 /* this did not seem to work with new compiler?! */ +#ifdef __i386__ +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + const int num_iters = 10; + int i; + unsigned int start, end; + u8 key[16], pt[16], ct[16]; + void *ctx; + + printf("keySetupEnc:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + 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(ctx, pt, ct); + rdtscll(end); + printf(" %d", end - start); + } + aes_encrypt_deinit(ctx); + printf("\n"); +#endif /* __i386__ */ +#endif +} + + +static int test_eax(void) +{ + u8 msg[] = { 0xF7, 0xFB }; + u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, + 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 }; + u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, + 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD }; + u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA }; + u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, + 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, + 0x67, 0xE5 }; + u8 data[sizeof(msg)], tag[BLOCK_SIZE]; + + memcpy(data, msg, sizeof(msg)); + if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode encryption failed\n"); + return 1; + } + if (memcmp(data, cipher, sizeof(data)) != 0) { + printf("AES-128 EAX mode encryption returned invalid cipher " + "text\n"); + return 1; + } + if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) { + printf("AES-128 EAX mode encryption returned invalid tag\n"); + return 1; + } + + if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode decryption failed\n"); + return 1; + } + if (memcmp(data, msg, sizeof(data)) != 0) { + printf("AES-128 EAX mode decryption returned invalid plain " + "text\n"); + return 1; + } + + return 0; +} + + +static int test_cbc(void) +{ + struct cbc_test_vector { + u8 key[16]; + u8 iv[16]; + u8 plain[32]; + u8 cipher[32]; + size_t len; + } vectors[] = { + { + { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, + "Single block msg", + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, + 16 + }, + { + { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, + 32 + } + }; + int ret = 0; + u8 *buf; + unsigned int i; + + for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) { + struct cbc_test_vector *tv = &vectors[i]; + buf = malloc(tv->len); + if (buf == NULL) { + ret++; + break; + } + memcpy(buf, tv->plain, tv->len); + aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->cipher, tv->len) != 0) { + printf("AES-CBC encrypt %d failed\n", i); + ret++; + } + memcpy(buf, tv->cipher, tv->len); + aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->plain, tv->len) != 0) { + printf("AES-CBC decrypt %d failed\n", i); + ret++; + } + free(buf); + } + + return ret; +} + + +/* OMAC1 AES-128 test vectors from + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf + * which are same as the examples from NIST SP800-38B + * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf + */ + +struct omac1_test_vector { + u8 k[16]; + u8 msg[64]; + int msg_len; + u8 tag[16]; +}; + +static struct omac1_test_vector test_vectors[] = +{ + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { }, + 0, + { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, + 16, + { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }, + 40, + { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }, + 64, + { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } + }, +}; + + +int main(int argc, char *argv[]) +{ + u8 kek[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + u8 plain[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt[] = { + 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, + 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, + 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 + }; + u8 result[24]; + int ret = 0; + unsigned int i; + struct omac1_test_vector *tv; + + 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++; + } + if (aes_unwrap(kek, 2, crypt, result)) { + printf("AES-UNWRAP-128-128 reported failure\n"); + ret++; + } + if (memcmp(result, plain, 16) != 0) { + printf("AES-UNWRAP-128-128 failed\n"); + ret++; + for (i = 0; i < 16; i++) + printf(" %02x", result[i]); + printf("\n"); + } + + test_aes_perf(); + + for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { + tv = &test_vectors[i]; + omac1_aes_128(tv->k, tv->msg, tv->msg_len, result); + if (memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128 test vector %d failed\n", i); + ret++; + } + + if (tv->msg_len > 1) { + const u8 *addr[2]; + size_t len[2]; + + addr[0] = tv->msg; + len[0] = 1; + addr[1] = tv->msg + 1; + len[1] = tv->msg_len - 1; + + omac1_aes_128_vector(tv->k, 2, addr, len, result); + if (memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128(vector) test vector %d " + "failed\n", i); + ret++; + } + } + } + + ret += test_eax(); + + ret += test_cbc(); + + if (ret) + printf("FAILED!\n"); + + return ret; +} diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c new file mode 100644 index 0000000000000..ee3eee4d2911d --- /dev/null +++ b/wpa_supplicant/tests/test_eap_sim_common.c @@ -0,0 +1,53 @@ +/* + * Test program for EAP-SIM PRF + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "eap_sim_common.c" + + +static int test_eap_sim_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); + eap_sim_prf(xkey, buf, sizeof(buf)); + if (memcmp(w, buf, sizeof(w) != 0)) { + printf("eap_sim_prf failed\n"); + return 1; + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + int errors = 0; + + errors += test_eap_sim_prf(); + + return errors; +} diff --git a/wpa_supplicant/tests/test_md4.c b/wpa_supplicant/tests/test_md4.c new file mode 100644 index 0000000000000..e92e9a5fafdd4 --- /dev/null +++ b/wpa_supplicant/tests/test_md4.c @@ -0,0 +1,99 @@ +/* + * Test program for MD4 (test vectors from RFC 1320) + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + +int main(int argc, char *argv[]) +{ + struct { + char *data; + u8 *hash; + } tests[] = { + { + "", + "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" + "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0" + }, + { + "a", + "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46" + "\x24\x5e\x05\xfb\xdb\xd6\xfb\x24" + }, + { + "abc", + "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" + "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d" + }, + { + "message digest", + "\xd9\x13\x0a\x81\x64\x54\x9f\xe8" + "\x18\x87\x48\x06\xe1\xc7\x01\x4b" + }, + { + "abcdefghijklmnopqrstuvwxyz", + "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd" + "\xee\xa8\xed\x63\xdf\x41\x2d\xa9" + }, + { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "\x04\x3f\x85\x82\xf2\x41\xdb\x35" + "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4" + }, + { + "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19" + "\x9c\x3e\x7b\x16\x4f\xcc\x05\x36" + } + }; + unsigned int i; + u8 hash[16]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + printf("MD4 test case %d:", i); + + addr[0] = tests[i].data; + len[0] = strlen(tests[i].data); + md4_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = tests[i].data; + len[0] = strlen(tests[i].data); + addr[1] = tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + md4_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + return errors; +} diff --git a/wpa_supplicant/tests/test_md5.c b/wpa_supplicant/tests/test_md5.c new file mode 100644 index 0000000000000..d8fb41ef694ed --- /dev/null +++ b/wpa_supplicant/tests/test_md5.c @@ -0,0 +1,99 @@ +/* + * Test program for MD5 (test vectors from RFC 1321) + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + +int main(int argc, char *argv[]) +{ + struct { + char *data; + u8 *hash; + } tests[] = { + { + "", + "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" + "\xe9\x80\x09\x98\xec\xf8\x42\x7e" + }, + { + "a", + "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8" + "\x31\xc3\x99\xe2\x69\x77\x26\x61" + }, + { + "abc", + "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" + "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72" + }, + { + "message digest", + "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d" + "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0" + }, + { + "abcdefghijklmnopqrstuvwxyz", + "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00" + "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b" + }, + { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" + "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f" + }, + { + "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55" + "\xac\x49\xda\x2e\x21\x07\xb6\x7a" + } + }; + unsigned int i; + u8 hash[16]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + printf("MD5 test case %d:", i); + + addr[0] = tests[i].data; + len[0] = strlen(tests[i].data); + md5_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = tests[i].data; + len[0] = strlen(tests[i].data); + addr[1] = tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + md5_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + return errors; +} diff --git a/wpa_supplicant/tests/test_ms_funcs.c b/wpa_supplicant/tests/test_ms_funcs.c new file mode 100644 index 0000000000000..09b53c46f34f7 --- /dev/null +++ b/wpa_supplicant/tests/test_ms_funcs.c @@ -0,0 +1,119 @@ +/* + * Test program for ms_funcs + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "ms_funcs.c" + + +int main(int argc, char *argv[]) +{ + /* Test vector from RFC2759 example */ + u8 *username = "User"; + u8 *password = "clientPass"; + u8 auth_challenge[] = { + 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, + 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 + }; + u8 peer_challenge[] = { + 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, + 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E + }; + u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; + u8 password_hash[] = { + 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, + 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE + }; + u8 nt_response[] = { + 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, + 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, + 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF + }; + u8 password_hash_hash[] = { + 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, + 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F + }; + u8 authenticator_response[] = { + 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, + 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, + 0x93, 0x2C, 0xDA, 0x56 + }; + u8 master_key[] = { + 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, + 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 + }; + u8 send_start_key[] = { + 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, + 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB + }; + u8 buf[32]; + + int errors = 0; + + printf("Testing ms_funcs.c\n"); + + challenge_hash(peer_challenge, auth_challenge, + username, strlen(username), + buf); + if (memcmp(challenge, buf, sizeof(challenge)) != 0) { + printf("challenge_hash failed\n"); + errors++; + } + + nt_password_hash(password, strlen(password), buf); + if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { + printf("nt_password_hash failed\n"); + errors++; + } + + generate_nt_response(auth_challenge, peer_challenge, + username, strlen(username), + password, strlen(password), + buf); + if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { + printf("generate_nt_response failed\n"); + errors++; + } + + hash_nt_password_hash(password_hash, buf); + if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { + printf("hash_nt_password_hash failed\n"); + errors++; + } + + generate_authenticator_response(password, strlen(password), + peer_challenge, auth_challenge, + username, strlen(username), + nt_response, buf); + if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) + != 0) { + printf("generate_authenticator_response failed\n"); + errors++; + } + + get_master_key(password_hash_hash, nt_response, buf); + if (memcmp(master_key, buf, sizeof(master_key)) != 0) { + printf("get_master_key failed\n"); + errors++; + } + + get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); + if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { + printf("get_asymetric_start_key failed\n"); + errors++; + } + + if (errors) + printf("FAILED! %d errors\n", errors); + + return errors; +} diff --git a/wpa_supplicant/tests/test_sha1.c b/wpa_supplicant/tests/test_sha1.c new file mode 100644 index 0000000000000..d2e6a9942597d --- /dev/null +++ b/wpa_supplicant/tests/test_sha1.c @@ -0,0 +1,347 @@ +/* + * Test program for SHA1 and MD5 + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" + + +static int test_eap_fast(void) +{ + /* RFC 4851, Appendix B.1 */ + const u8 pac_key[] = { + 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09, + 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B, + 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA, + 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14 + }; + const u8 seed[] = { + 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A, + 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3, + 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93, + 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A, + 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A, + 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F, + 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A, + 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00 + }; + const u8 master_secret[] = { + 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02, + 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64, + 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77, + 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29, + 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, + 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 + }; + const u8 key_block[] = { + 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, + 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, + 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B, + 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57, + 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70, + 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB, + 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF, + 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44, + 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29, + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 sks[] = { + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 isk[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const u8 imck[] = { + 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9, + 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80, + 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96, + 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1, + 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5, + 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9, + 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8, + 0x15, 0xEC, 0x57, 0x7B + }; + const u8 msk[] = { + 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED, + 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33, + 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51, + 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9, + 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A, + 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49, + 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59, + 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3 + }; + const u8 emsk[] = { + 0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B, + 0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55, + 0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D, + 0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9, + 0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54, + 0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A, + 0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44, + 0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9 + }; + /* RFC 4851, Appendix B.2 */ + u8 tlv[] = { + 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00, + 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8, + 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14, + 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62, + 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58, + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + const u8 compound_mac[] = { + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + u8 buf[512]; + const u8 *simck, *cmk; + int errors = 0; + + printf("EAP-FAST test cases\n"); + + printf("- T-PRF (SHA1) test case / master_secret\n"); + sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash", + seed, sizeof(seed), buf, sizeof(master_secret)); + if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- PRF (TLS, SHA1/MD5) test case / key_block\n"); + tls_prf(master_secret, sizeof(master_secret), "key expansion", + seed, sizeof(seed), buf, sizeof(key_block)); + if (memcmp(key_block, buf, sizeof(key_block)) != 0) { + printf("PRF test - FAILED!\n"); + errors++; + } + + printf("- T-PRF (SHA1) test case / IMCK\n"); + sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", + isk, sizeof(isk), buf, sizeof(imck)); + if (memcmp(imck, buf, sizeof(imck)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + simck = imck; + cmk = imck + 40; + + printf("- T-PRF (SHA1) test case / MSK\n"); + sha1_t_prf(simck, 40, "Session Key Generating Function", + (u8 *) "", 0, buf, sizeof(msk)); + if (memcmp(msk, buf, sizeof(msk)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- T-PRF (SHA1) test case / EMSK\n"); + sha1_t_prf(simck, 40, "Extended Session Key Generating Function", + (u8 *) "", 0, buf, sizeof(msk)); + if (memcmp(emsk, buf, sizeof(emsk)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- Compound MAC test case\n"); + memset(tlv + sizeof(tlv) - 20, 0, 20); + hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20); + if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac)) + != 0) { + printf("Compound MAC test - FAILED!\n"); + errors++; + } + + return errors; +} + + +static u8 key0[] = +{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; +static u8 data0[] = "Hi There"; +static u8 prf0[] = +{ + 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, + 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, + 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06, + 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee, + 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88, + 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb, + 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e, + 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a +}; + +static u8 key1[] = "Jefe"; +static u8 data1[] = "what do ya want for nothing?"; +static u8 prf1[] = +{ + 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, + 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, + 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58, + 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09, + 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa, + 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02, + 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7, + 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc +}; + + +static u8 key2[] = +{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; +static u8 data2[] = +{ + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; +static u8 prf2[] = +{ + 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, + 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, + 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1, + 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce, + 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc, + 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae, + 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6, + 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07 +}; + + +struct passphrase_test { + char *passphrase; + char *ssid; + char psk[32]; +}; + +static struct passphrase_test passphrase_tests[] = +{ + { + "password", + "IEEE", + { + 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef, + 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90, + 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2, + 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e + } + }, + { + "ThisIsAPassword", + "ThisIsASSID", + { + 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6, + 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3, + 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08, + 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf + } + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", + { + 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83, + 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c, + 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48, + 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62 + } + }, +}; + +#define NUM_PASSPHRASE_TESTS \ +(sizeof(passphrase_tests) / sizeof(passphrase_tests[0])) + + +int main(int argc, char *argv[]) +{ + u8 res[512]; + int ret = 0; + unsigned int i; + + printf("PRF-SHA1 test cases:\n"); + + sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1, + res, sizeof(prf0)); + if (memcmp(res, prf0, sizeof(prf0)) == 0) + printf("Test case 0 - OK\n"); + else { + printf("Test case 0 - FAILED!\n"); + ret++; + } + + sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1, + res, sizeof(prf1)); + if (memcmp(res, prf1, sizeof(prf1)) == 0) + printf("Test case 1 - OK\n"); + else { + printf("Test case 1 - FAILED!\n"); + ret++; + } + + sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2), + res, sizeof(prf2)); + if (memcmp(res, prf2, sizeof(prf2)) == 0) + printf("Test case 2 - OK\n"); + else { + printf("Test case 2 - FAILED!\n"); + ret++; + } + + ret += test_eap_fast(); + + printf("PBKDF2-SHA1 Passphrase test cases:\n"); + for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { + u8 psk[32]; + struct passphrase_test *test = &passphrase_tests[i]; + pbkdf2_sha1(test->passphrase, + test->ssid, strlen(test->ssid), + 4096, psk, 32); + if (memcmp(psk, test->psk, 32) == 0) + printf("Test case %d - OK\n", i); + else { + printf("Test case %d - FAILED!\n", i); + ret++; + } + } + + return ret; +} diff --git a/wpa_supplicant/tests/test_sha256.c b/wpa_supplicant/tests/test_sha256.c new file mode 100644 index 0000000000000..7dc460d483e8e --- /dev/null +++ b/wpa_supplicant/tests/test_sha256.c @@ -0,0 +1,331 @@ +/* + * Test program for SHA256 + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + +struct { + char *data; + u8 hash[32]; +} tests[] = { + { + "abc", + { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + } + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { + 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 + } + } +}; + +struct hmac_test { + u8 key[80]; + size_t key_len; + u8 data[128]; + size_t data_len; + u8 hash[32]; +} hmac_tests[] = { + /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abc", 3, + { + 0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a, + 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, + 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, + 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + { + 0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08, + 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, + 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, + 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 112, + { + 0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34, + 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, + 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, + 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 + } + }, + { + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }, + 32, + "Hi There", + 8, + { + 0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6, + 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, + 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, + 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + } + }, + { + "Jefe", + 4, + "what do ya want for nothing?", + 28, + { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, + 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, + 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 32, + { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }, + 50, + { + 0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea, + 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, + 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, + 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25 + }, + 37, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74, + 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, + 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, + 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + } + }, + { + { + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c + }, + 32, + "Test With Truncation", + 20, + { + 0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b, + 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, + 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, + 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09, + 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, + 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, + 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + 73, + { + 0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3, + 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, + 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, + 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + } + } +}; + + +int main(int argc, char *argv[]) +{ + + unsigned int i; + u8 hash[32]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + printf("SHA256 test case %d:", i + 1); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + sha256_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = 1; + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + sha256_vector(2, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + for (i = 0; i < sizeof(hmac_tests) / sizeof(hmac_tests[0]); i++) { + struct hmac_test *t = &hmac_tests[i]; + printf("HMAC-SHA256 test case %d:", i + 1); + + hmac_sha256(t->key, t->key_len, t->data, t->data_len, hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + addr[0] = t->data; + len[0] = t->data_len; + hmac_sha256_vector(t->key, t->key_len, 1, addr, len, hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + hmac_sha256_vector(t->key, t->key_len, 2, addr, len, + hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + printf("Test IEEE 802.11r KDF\n"); + sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4, + hash, sizeof(hash)); + /* TODO: add proper test case for this */ + + return errors; +} diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c new file mode 100644 index 0000000000000..74bb5f8c9adfe --- /dev/null +++ b/wpa_supplicant/tests/test_wpa.c @@ -0,0 +1,394 @@ +/* + * Test program for combined WPA authenticator/supplicant + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "config.h" +#include "wpa.h" +#include "wpa_ie.h" +#include "../hostapd/wpa.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + + +struct wpa { + u8 auth_addr[ETH_ALEN]; + u8 supp_addr[ETH_ALEN]; + u8 psk[PMK_LEN]; + + /* from authenticator */ + u8 auth_eapol_dst[ETH_ALEN]; + u8 *auth_eapol; + size_t auth_eapol_len; + + /* from supplicant */ + u8 *supp_eapol; + size_t supp_eapol_len; + + struct wpa_sm *supp; + struct wpa_authenticator *auth_group; + struct wpa_state_machine *auth; + + struct wpa_ssid ssid; + u8 supp_ie[80]; + size_t supp_ie_len; +}; + + +static struct wpa_ssid * supp_get_ssid(void *ctx) +{ + struct wpa *wpa = ctx; + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + return &wpa->ssid; +} + + +static int supp_get_bssid(void *ctx, u8 *bssid) +{ + struct wpa *wpa = ctx; + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + os_memcpy(bssid, wpa->auth_addr, ETH_ALEN); + return 0; +} + + +static void supp_set_state(void *ctx, wpa_states state) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state); +} + + +static void auth_eapol_rx(void *eloop_data, void *user_ctx) +{ + struct wpa *wpa = eloop_data; + + wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame"); + wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol, + wpa->supp_eapol_len); +} + + +static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len) +{ + struct wpa *wpa = ctx; + + wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " + "len=%lu)", + __func__, MAC2STR(dest), proto, (unsigned long) len); + + os_free(wpa->supp_eapol); + wpa->supp_eapol = os_malloc(len); + if (wpa->supp_eapol == NULL) + return -1; + os_memcpy(wpa->supp_eapol, buf, len); + wpa->supp_eapol_len = len; + eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL); + + return 0; +} + + +static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, + u16 data_len, size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", + __func__, type, data_len); + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = 2; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +static int supp_get_beacon_ie(void *ctx) +{ + struct wpa *wpa = ctx; + const u8 *ie; + size_t ielen; + + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + + ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen); + if (ie == NULL || ielen < 1) + return -1; + if (ie[0] == WLAN_EID_RSN) + return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]); + return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]); +} + + +static int supp_set_key(void *ctx, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " + "set_tx=%d)", + __func__, alg, MAC2STR(addr), key_idx, set_tx); + wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); + wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len); + return 0; +} + + +static int supp_mlme_setprotection(void *ctx, const u8 *addr, + int protection_type, int key_type) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " + "key_type=%d)", + __func__, MAC2STR(addr), protection_type, key_type); + return 0; +} + + +static void supp_cancel_scan(void *ctx) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); +} + + +static void supp_cancel_auth_timeout(void *ctx) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); +} + + +static int supp_init(struct wpa *wpa) +{ + struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->ctx = wpa; + ctx->set_state = supp_set_state; + ctx->get_ssid = supp_get_ssid; + ctx->get_bssid = supp_get_bssid; + ctx->ether_send = supp_ether_send; + ctx->get_beacon_ie = supp_get_beacon_ie; + ctx->alloc_eapol = supp_alloc_eapol; + ctx->set_key = supp_set_key; + ctx->mlme_setprotection = supp_mlme_setprotection; + ctx->cancel_scan = supp_cancel_scan; + ctx->cancel_auth_timeout = supp_cancel_auth_timeout; + wpa->supp = wpa_sm_init(ctx); + if (wpa->supp == NULL) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); + return -1; + } + + wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr); + wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1); + wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); + wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); + wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); + wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); + wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN); + + wpa->supp_ie_len = sizeof(wpa->supp_ie); + if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie, + &wpa->supp_ie_len) < 0) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" + " failed"); + return -1; + } + + wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr); + + return 0; +} + + +static void auth_logger(void *ctx, const u8 *addr, logger_level level, + const char *txt) +{ + if (addr) + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "AUTH: %s", txt); +} + + +static void supp_eapol_rx(void *eloop_data, void *user_ctx) +{ + struct wpa *wpa = eloop_data; + + wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame"); + wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol, + wpa->auth_eapol_len); +} + + +static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt) +{ + struct wpa *wpa = ctx; + + wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " + "encrypt=%d)", + __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); + + os_free(wpa->auth_eapol); + wpa->auth_eapol = os_malloc(data_len); + if (wpa->auth_eapol == NULL) + return -1; + os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN); + os_memcpy(wpa->auth_eapol, data, data_len); + wpa->auth_eapol_len = data_len; + eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL); + + return 0; +} + + +static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) +{ + struct wpa *wpa = ctx; + wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", + __func__, MAC2STR(addr), prev_psk); + if (prev_psk) + return NULL; + return wpa->psk; +} + + +static int auth_init_group(struct wpa *wpa) +{ + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); + + os_memset(&conf, 0, sizeof(conf)); + conf.wpa = 2; + conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; + conf.wpa_pairwise = WPA_CIPHER_CCMP; + conf.rsn_pairwise = WPA_CIPHER_CCMP; + conf.wpa_group = WPA_CIPHER_CCMP; + conf.eapol_version = 2; + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = wpa; + cb.logger = auth_logger; + cb.send_eapol = auth_send_eapol; + cb.get_psk = auth_get_psk; + + wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb); + if (wpa->auth_group == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); + return -1; + } + + return 0; +} + + +static int auth_init(struct wpa *wpa) +{ + wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr); + if (wpa->auth == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); + return -1; + } + + if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie, + wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); + return -1; + } + + wpa_auth_sm_event(wpa->auth, WPA_ASSOC); + + wpa_auth_sta_associated(wpa->auth_group, wpa->auth); + + return 0; +} + + +static void deinit(struct wpa *wpa) +{ + wpa_auth_sta_deinit(wpa->auth); + wpa_sm_deinit(wpa->supp); + wpa_deinit(wpa->auth_group); + os_free(wpa->auth_eapol); + wpa->auth_eapol = NULL; + os_free(wpa->supp_eapol); + wpa->supp_eapol = NULL; +} + + +int main(int argc, char *argv[]) +{ + struct wpa wpa; + + if (os_program_init()) + return -1; + + os_memset(&wpa, 0, sizeof(wpa)); + os_memset(wpa.auth_addr, 0x12, ETH_ALEN); + os_memset(wpa.supp_addr, 0x32, ETH_ALEN); + os_memset(wpa.psk, 0x44, PMK_LEN); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (eloop_init(&wpa)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + if (auth_init_group(&wpa) < 0) + return -1; + + if (supp_init(&wpa) < 0) + return -1; + + if (auth_init(&wpa) < 0) + return -1; + + wpa_printf(MSG_DEBUG, "Starting eloop"); + eloop_run(); + wpa_printf(MSG_DEBUG, "eloop done"); + + deinit(&wpa); + + eloop_destroy(); + + os_program_deinit(); + + return 0; +} diff --git a/wpa_supplicant/tests/test_x509v3.c b/wpa_supplicant/tests/test_x509v3.c new file mode 100644 index 0000000000000..c472c8a660746 --- /dev/null +++ b/wpa_supplicant/tests/test_x509v3.c @@ -0,0 +1,69 @@ +/* + * Testing tool for X.509v3 routines + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "tls/asn1.h" +#include "tls/x509v3.h" + +extern int wpa_debug_level; + + +int main(int argc, char *argv[]) +{ + char *buf; + size_t len; + struct x509_certificate *certs = NULL, *last = NULL, *cert; + int i, reason; + + wpa_debug_level = 0; + + if (argc < 3 || strcmp(argv[1], "-v") != 0) { + printf("usage: test_x509v3 -v <cert1.der> <cert2.der> ..\n"); + return -1; + } + + for (i = 2; i < argc; i++) { + printf("Reading: %s\n", argv[i]); + buf = os_readfile(argv[i], &len); + if (buf == NULL) { + printf("Failed to read '%s'\n", argv[i]); + return -1; + } + + cert = x509_certificate_parse((u8 *) buf, len); + if (cert == NULL) { + printf("Failed to parse X.509 certificate\n"); + return -1; + } + + free(buf); + + if (certs == NULL) + certs = cert; + else + last->next = cert; + last = cert; + } + + printf("\n\nValidating certificate chain\n"); + if (x509_certificate_chain_validate(last, certs, &reason) < 0) { + printf("\nCertificate chain validation failed: %d\n", reason); + return -1; + } + printf("\nCertificate chain is valid\n"); + + return 0; +} diff --git a/wpa_supplicant/tests/test_x509v3_nist.sh b/wpa_supplicant/tests/test_x509v3_nist.sh new file mode 100755 index 0000000000000..c33e36289dcbf --- /dev/null +++ b/wpa_supplicant/tests/test_x509v3_nist.sh @@ -0,0 +1,144 @@ +#!/bin/sh + +# X.509 Path Validation Test Suite, Version 1.07 +# http://csrc.nist.gov/pki/testing/x509paths_old.html +# http://csrc.nist.gov/pki/testing/x509tests.tgz + +if [ -z "$1" ]; then + echo "usage: $0 <path to X509tests directory>" + exit 1 +fi + +TESTS=$1 + +if [ ! -d $TESTS ]; then + echo "Not a directory: $TESTS" + exit 1 +fi + +X509TEST="./test_x509v3 -v" +TMPOUT=test_x509v3_nist.out + +# TODO: add support for validating CRLs + +END="End Certificate " +ROOT="Trust Anchor " +ICA="Intermediate Certificate " + +SUCCESS="" +FAILURE="" + +function run_test +{ + NUM=$1 + RES=$2 + shift 2 + $X509TEST "$@" > $TMPOUT.$NUM + VALRES=$? + OK=0 + if [ $RES -eq 0 ]; then + # expecting success + if [ $VALRES -eq 0 ]; then + OK=1 + else + echo "test$NUM failed - expected validation success" + OK=0 + fi + else + # expecting failure + if [ $VALRES -eq 0 ]; then + echo "test$NUM failed - expected validation failure" + OK=0 + else + REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM` + if [ $? -eq 0 ]; then + REASONNUM=`echo "$REASON" | colrm 1 37` + if [ $REASONNUM -eq $RES ]; then + OK=1 + else + echo "test$NUM failed - expected validation result $RES; result was $REASONNUM" + OK=0 + fi + else + echo "test$NUM failed - expected validation failure; other type of error detected" + OK=0 + fi + fi + fi + if [ $OK -eq 1 ]; then + rm $TMPOUT.$NUM + SUCCESS="$SUCCESS $NUM" + else + FAILURE="$FAILURE $NUM" + fi +} + +P=$TESTS/test + +run_test 1 0 "${P}1/${END}CP.01.01.crt" "${P}1/${ROOT}CP.01.01.crt" +run_test 2 1 "${P}2/${END}CP.01.02.crt" "${P}2/${ICA}CP.01.02.crt" "${P}2/${ROOT}CP.01.01.crt" +run_test 3 1 "${P}3/${END}CP.01.03.crt" "${P}3/${ICA}CP.01.03.crt" "${P}3/${ROOT}CP.01.01.crt" +run_test 4 0 "${P}4/${END}CP.02.01.crt" "${P}4/${ICA}2 CP.02.01.crt" "${P}4/${ICA}1 CP.02.01.crt" "${P}4/${ROOT}CP.01.01.crt" +run_test 5 4 "${P}5/${END}CP.02.02.crt" "${P}5/${ICA}CP.02.02.crt" "${P}5/${ROOT}CP.01.01.crt" +run_test 6 4 "${P}6/${END}CP.02.03.crt" "${P}6/${ICA}CP.02.03.crt" "${P}6/${ROOT}CP.01.01.crt" +run_test 7 0 "${P}7/${END}CP.02.04.crt" "${P}7/${ICA}CP.02.04.crt" "${P}7/${ROOT}CP.01.01.crt" +run_test 8 4 "${P}8/${END}CP.02.05.crt" "${P}8/${ICA}CP.02.05.crt" "${P}8/${ROOT}CP.01.01.crt" +run_test 9 4 "${P}9/${END}CP.03.01.crt" "${P}9/${ICA}CP.03.01.crt" "${P}9/${ROOT}CP.01.01.crt" +run_test 10 4 "${P}10/${END}CP.03.02.crt" "${P}10/${ICA}CP.03.02.crt" "${P}10/${ROOT}CP.01.01.crt" +run_test 11 4 "${P}11/${END}CP.03.03.crt" "${P}11/${ICA}CP.03.03.crt" "${P}11/${ROOT}CP.01.01.crt" +run_test 12 0 "${P}12/${END}CP.03.04.crt" "${P}12/${ICA}CP.03.04.crt" "${P}12/${ROOT}CP.01.01.crt" +run_test 13 5 "${P}13/${END}CP.04.01.crt" "${P}13/${ICA}CP.04.01.crt" "${P}13/${ROOT}CP.01.01.crt" +run_test 14 5 "${P}14/${END}CP.04.02.crt" "${P}14/${ICA}CP.04.02.crt" "${P}14/${ROOT}CP.01.01.crt" +run_test 15 0 "${P}15/${END}CP.04.03.crt" "${P}15/${ICA}CP.04.03.crt" "${P}15/${ROOT}CP.01.01.crt" +run_test 16 0 "${P}16/${END}CP.04.04.crt" "${P}16/${ICA}CP.04.04.crt" "${P}16/${ROOT}CP.01.01.crt" +run_test 17 0 "${P}17/${END}CP.04.05.crt" "${P}17/${ICA}CP.04.05.crt" "${P}17/${ROOT}CP.01.01.crt" +run_test 18 0 "${P}18/${END}CP.04.06.crt" "${P}18/${ICA}CP.04.06.crt" "${P}18/${ROOT}CP.01.01.crt" +run_test 19 1 "${P}19/${END}CP.05.01.crt" "${P}19/${ICA}CP.05.01.crt" "${P}19/${ROOT}CP.01.01.crt" +run_test 20 3 "${P}20/${END}CP.06.01.crt" "${P}20/${ICA}CP.06.01.crt" "${P}20/${ROOT}CP.01.01.crt" +run_test 21 3 "${P}21/${END}CP.06.02.crt" "${P}21/${ICA}CP.06.02.crt" "${P}21/${ROOT}CP.01.01.crt" +run_test 22 1 "${P}22/${END}IC.01.01.crt" "${P}22/${ICA}IC.01.01.crt" "${P}22/${ROOT}CP.01.01.crt" +run_test 23 1 "${P}23/${END}IC.02.01.crt" "${P}23/${ICA}IC.02.01.crt" "${P}23/${ROOT}CP.01.01.crt" +run_test 24 0 "${P}24/${END}IC.02.02.crt" "${P}24/${ICA}IC.02.02.crt" "${P}24/${ROOT}CP.01.01.crt" +run_test 25 1 "${P}25/${END}IC.02.03.crt" "${P}25/${ICA}IC.02.03.crt" "${P}25/${ROOT}CP.01.01.crt" +run_test 26 0 "${P}26/${END}IC.02.04.crt" "${P}26/${ICA}IC.02.04.crt" "${P}26/${ROOT}CP.01.01.crt" +run_test 27 0 "${P}27/${END}IC.04.01.crt" "${P}27/${ICA}IC.04.01.crt" "${P}27/${ROOT}CP.01.01.crt" +run_test 28 1 "${P}28/${END}IC.05.01.crt" "${P}28/${ICA}IC.05.01.crt" "${P}28/${ROOT}CP.01.01.crt" +run_test 29 1 "${P}29/${END}IC.05.02.crt" "${P}29/${ICA}IC.05.02.crt" "${P}29/${ROOT}CP.01.01.crt" +run_test 30 0 "${P}30/${END}IC.05.03.crt" "${P}30/${ICA}IC.05.03.crt" "${P}30/${ROOT}CP.01.01.crt" +run_test 31 1 "${P}31/${END}IC.06.01.crt" "${P}31/${ICA}IC.06.01.crt" "${P}31/${ROOT}CP.01.01.crt" +run_test 32 1 "${P}32/${END}IC.06.02.crt" "${P}32/${ICA}IC.06.02.crt" "${P}32/${ROOT}CP.01.01.crt" +run_test 33 0 "${P}33/${END}IC.06.03.crt" "${P}33/${ICA}IC.06.03.crt" "${P}33/${ROOT}CP.01.01.crt" +run_test 34 0 "${P}34/${END}PP.01.01.crt" "${P}34/${ICA}PP.01.01.crt" "${P}34/${ROOT}CP.01.01.crt" +run_test 35 0 "${P}35/${END}PP.01.02.crt" "${P}35/${ICA}PP.01.02.crt" "${P}35/${ROOT}CP.01.01.crt" +run_test 36 0 "${P}36/${END}PP.01.03.crt" "${P}36/${ICA}2 PP.01.03.crt" "${P}36/${ICA}1 PP.01.03.crt" "${P}36/${ROOT}CP.01.01.crt" +run_test 37 0 "${P}37/${END}PP.01.04.crt" "${P}37/${ICA}2 PP.01.04.crt" "${P}37/${ICA}1 PP.01.04.crt" "${P}37/${ROOT}CP.01.01.crt" +run_test 38 0 "${P}38/${END}PP.01.05.crt" "${P}38/${ICA}2 PP.01.05.crt" "${P}38/${ICA}1 PP.01.05.crt" "${P}38/${ROOT}CP.01.01.crt" +run_test 39 0 "${P}39/${END}PP.01.06.crt" "${P}39/${ICA}3 PP.01.06.crt" "${P}39/${ICA}2 PP.01.06.crt" "${P}39/${ICA}1 PP.01.06.crt" "${P}39/${ROOT}CP.01.01.crt" +run_test 40 0 "${P}40/${END}PP.01.07.crt" "${P}40/${ICA}3 PP.01.07.crt" "${P}40/${ICA}2 PP.01.07.crt" "${P}40/${ICA}1 PP.01.07.crt" "${P}40/${ROOT}CP.01.01.crt" +run_test 41 0 "${P}41/${END}PP.01.08.crt" "${P}41/${ICA}3 PP.01.08.crt" "${P}41/${ICA}2 PP.01.08.crt" "${P}41/${ICA}1 PP.01.08.crt" "${P}41/${ROOT}CP.01.01.crt" +run_test 42 0 "${P}42/${END}PP.01.09.crt" "${P}42/${ICA}4 PP.01.09.crt" "${P}42/${ICA}3 PP.01.09.crt" "${P}42/${ICA}2 PP.01.09.crt" "${P}42/${ICA}1 PP.01.09.crt" "${P}42/${ROOT}CP.01.01.crt" +run_test 43 0 "${P}43/${END}PP.06.01.crt" "${P}43/${ICA}4 PP.06.01.crt" "${P}43/${ICA}3 PP.06.01.crt" "${P}43/${ICA}2 PP.06.01.crt" "${P}43/${ICA}1 PP.06.01.crt" "${P}43/${ROOT}CP.01.01.crt" +run_test 44 0 "${P}44/${END}PP.06.02.crt" "${P}44/${ICA}4 PP.06.02.crt" "${P}44/${ICA}3 PP.06.02.crt" "${P}44/${ICA}2 PP.06.02.crt" "${P}44/${ICA}1 PP.06.02.crt" "${P}44/${ROOT}CP.01.01.crt" +run_test 45 0 "${P}45/${END}PP.06.03.crt" "${P}45/${ICA}4 PP.06.03.crt" "${P}45/${ICA}3 PP.06.03.crt" "${P}45/${ICA}2 PP.06.03.crt" "${P}45/${ICA}1 PP.06.03.crt" "${P}45/${ROOT}CP.01.01.crt" +run_test 46 0 "${P}46/${END}PP.06.04.crt" "${P}46/${ICA}4 PP.06.04.crt" "${P}46/${ICA}3 PP.06.04.crt" "${P}46/${ICA}2 PP.06.04.crt" "${P}46/${ICA}1 PP.06.04.crt" "${P}46/${ROOT}CP.01.01.crt" +run_test 47 0 "${P}47/${END}PP.06.05.crt" "${P}47/${ICA}4 PP.06.05.crt" "${P}47/${ICA}3 PP.06.05.crt" "${P}47/${ICA}2 PP.06.05.crt" "${P}47/${ICA}1 PP.06.05.crt" "${P}47/${ROOT}CP.01.01.crt" +run_test 48 0 "${P}48/${END}PP.08.01.crt" "${P}48/${ICA}PP.08.01.crt" "${P}48/${ROOT}CP.01.01.crt" +run_test 49 0 "${P}49/${END}PP.08.02.crt" "${P}49/${ICA}PP.08.02.crt" "${P}49/${ROOT}CP.01.01.crt" +run_test 50 0 "${P}50/${END}PP.08.03.crt" "${P}50/${ICA}PP.08.03.crt" "${P}50/${ROOT}CP.01.01.crt" +run_test 51 0 "${P}51/${END}PP.08.04.crt" "${P}51/${ICA}PP.08.04.crt" "${P}51/${ROOT}CP.01.01.crt" +run_test 52 0 "${P}52/${END}PP.08.05.crt" "${P}52/${ICA}PP.08.05.crt" "${P}52/${ROOT}CP.01.01.crt" +run_test 53 0 "${P}53/${END}PP.08.06.crt" "${P}53/${ICA}PP.08.06.crt" "${P}53/${ROOT}CP.01.01.crt" +run_test 54 1 "${P}54/${END}PL.01.01.crt" "${P}54/${ICA}2 PL.01.01.crt" "${P}54/${ICA}1 PL.01.01.crt" "${P}54/${ROOT}CP.01.01.crt" +run_test 55 1 "${P}55/${END}PL.01.02.crt" "${P}55/${ICA}2 PL.01.02.crt" "${P}55/${ICA}1 PL.01.02.crt" "${P}55/${ROOT}CP.01.01.crt" +run_test 56 0 "${P}56/${END}PL.01.03.crt" "${P}56/${ICA}PL.01.03.crt" "${P}56/${ROOT}CP.01.01.crt" +run_test 57 0 "${P}57/${END}PL.01.04.crt" "${P}57/${ICA}PL.01.04.crt" "${P}57/${ROOT}CP.01.01.crt" +run_test 58 1 "${P}58/${END}PL.01.05.crt" "${P}58/${ICA}3 PL.01.05.crt" "${P}58/${ICA}2 PL.01.05.crt" "${P}58/${ICA}1 PL.01.05.crt" "${P}58/${ROOT}CP.01.01.crt" +run_test 59 1 "${P}59/${END}PL.01.06.crt" "${P}59/${ICA}3 PL.01.06.crt" "${P}59/${ICA}2 PL.01.06.crt" "${P}59/${ICA}1 PL.01.06.crt" "${P}59/${ROOT}CP.01.01.crt" +run_test 60 1 "${P}60/${END}PL.01.07.crt" "${P}60/${ICA}4 PL.01.07.crt" "${P}60/${ICA}3 PL.01.07.crt" "${P}60/${ICA}2 PL.01.07.crt" "${P}60/${ICA}1 PL.01.07.crt" "${P}60/${ROOT}CP.01.01.crt" +run_test 61 1 "${P}61/${END}PL.01.08.crt" "${P}61/${ICA}4 PL.01.08.crt" "${P}61/${ICA}3 PL.01.08.crt" "${P}61/${ICA}2 PL.01.08.crt" "${P}61/${ICA}1 PL.01.08.crt" "${P}61/${ROOT}CP.01.01.crt" +run_test 62 0 "${P}62/${END}PL.01.09.crt" "${P}62/${ICA}4 PL.01.09.crt" "${P}62/${ICA}3 PL.01.09.crt" "${P}62/${ICA}2 PL.01.09.crt" "${P}62/${ICA}1 PL.01.09.crt" "${P}62/${ROOT}CP.01.01.crt" +run_test 63 0 "${P}63/${END}PL.01.10.crt" "${P}63/${ICA}4 PL.01.10.crt" "${P}63/${ICA}3 PL.01.10.crt" "${P}63/${ICA}2 PL.01.10.crt" "${P}63/${ICA}1 PL.01.10.crt" "${P}63/${ROOT}CP.01.01.crt" + + +echo "Successful tests:$SUCCESS" +echo "Failed tests:$FAILURE" diff --git a/wpa_supplicant/tests/test_x509v3_nist2.sh b/wpa_supplicant/tests/test_x509v3_nist2.sh new file mode 100755 index 0000000000000..0be29b7feb31f --- /dev/null +++ b/wpa_supplicant/tests/test_x509v3_nist2.sh @@ -0,0 +1,165 @@ +#!/bin/sh + +# Public Key Interoperability Test Suite (PKITS) +# http://csrc.nist.gov/pki/testing/x509paths.html +# http://csrc.nist.gov/pki/testing/PKITS_data.zip + +if [ -z "$1" ]; then + echo "usage: $0 <path to root test directory>" + exit 1 +fi + +TESTS=$1 + +if [ ! -d $TESTS ]; then + echo "Not a directory: $TESTS" + exit 1 +fi + +X509TEST="$PWD/test_x509v3 -v" +TMPOUT="$PWD/test_x509v3_nist2.out" + +# TODO: add support for validating CRLs + +SUCCESS="" +FAILURE="" + +function run_test +{ + NUM=$1 + RES=$2 + shift 2 + $X509TEST "$@" TrustAnchorRootCertificate.crt > $TMPOUT.$NUM + VALRES=$? + OK=0 + if [ $RES -eq 0 ]; then + # expecting success + if [ $VALRES -eq 0 ]; then + OK=1 + else + echo "$NUM failed - expected validation success" + OK=0 + fi + else + # expecting failure + if [ $VALRES -eq 0 ]; then + echo "$NUM failed - expected validation failure" + OK=0 + else + REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM` + if [ $? -eq 0 ]; then + REASONNUM=`echo "$REASON" | colrm 1 37` + if [ $REASONNUM -eq $RES ]; then + OK=1 + else + echo "$NUM failed - expected validation result $RES; result was $REASONNUM" + OK=0 + fi + else + echo "$NUM failed - expected validation failure; other type of error detected" + OK=0 + fi + fi + fi + if [ $OK -eq 1 ]; then + rm $TMPOUT.$NUM + SUCCESS="$SUCCESS $NUM" + else + FAILURE="$FAILURE $NUM" + fi +} + +pushd $TESTS/certs + +run_test 4.1.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt +run_test 4.1.2 1 InvalidCASignatureTest2EE.crt BadSignedCACert.crt +run_test 4.1.3 1 InvalidEESignatureTest3EE.crt GoodCACert.crt + +run_test 4.2.1 4 InvalidCAnotBeforeDateTest1EE.crt BadnotBeforeDateCACert.crt +run_test 4.2.2 4 InvalidEEnotBeforeDateTest2EE.crt GoodCACert.crt +run_test 4.2.3 0 Validpre2000UTCnotBeforeDateTest3EE.crt GoodCACert.crt +run_test 4.2.4 0 ValidGeneralizedTimenotBeforeDateTest4EE.crt GoodCACert.crt +run_test 4.2.5 4 InvalidCAnotAfterDateTest5EE.crt BadnotAfterDateCACert.crt +run_test 4.2.6 4 InvalidEEnotAfterDateTest6EE.crt GoodCACert.crt +run_test 4.2.7 4 Invalidpre2000UTCEEnotAfterDateTest7EE.crt GoodCACert.crt +run_test 4.2.8 0 ValidGeneralizedTimenotAfterDateTest8EE.crt GoodCACert.crt + +run_test 4.3.1 5 InvalidNameChainingTest1EE.crt GoodCACert.crt +run_test 4.3.2 5 InvalidNameChainingOrderTest2EE.crt NameOrderingCACert.crt +run_test 4.3.3 0 ValidNameChainingWhitespaceTest3EE.crt GoodCACert.crt +run_test 4.3.4 0 ValidNameChainingWhitespaceTest4EE.crt GoodCACert.crt +run_test 4.3.5 0 ValidNameChainingCapitalizationTest5EE.crt GoodCACert.crt +run_test 4.3.6 0 ValidNameUIDsTest6EE.crt UIDCACert.crt +run_test 4.3.7 0 ValidRFC3280MandatoryAttributeTypesTest7EE.crt RFC3280MandatoryAttributeTypesCACert.crt +run_test 4.3.8 0 ValidRFC3280OptionalAttributeTypesTest8EE.crt RFC3280OptionalAttributeTypesCACert.crt +run_test 4.3.9 0 ValidUTF8StringEncodedNamesTest9EE.crt UTF8StringEncodedNamesCACert.crt +run_test 4.3.10 0 ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt RolloverfromPrintableStringtoUTF8StringCACert.crt +run_test 4.3.11 0 ValidUTF8StringCaseInsensitiveMatchTest11EE.crt UTF8StringCaseInsensitiveMatchCACert.crt + +run_test 4.4.1 1 InvalidMissingCRLTest1EE.crt NoCRLCACert.crt +# skip rest of 4.4.x tests since CRLs are not yet supported + +run_test 4.5.1 0 ValidBasicSelfIssuedOldWithNewTest1EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt +run_test 4.5.2 3 InvalidBasicSelfIssuedOldWithNewTest2EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt +run_test 4.5.3 0 ValidBasicSelfIssuedNewWithOldTest3EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.4 0 ValidBasicSelfIssuedNewWithOldTest4EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.5 3 InvalidBasicSelfIssuedNewWithOldTest5EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.6 0 ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt +run_test 4.5.7 3 InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt +run_test 4.5.8 1 InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt + +run_test 4.6.1 1 InvalidMissingbasicConstraintsTest1EE.crt MissingbasicConstraintsCACert.crt +run_test 4.6.2 1 InvalidcAFalseTest2EE.crt basicConstraintsCriticalcAFalseCACert.crt +run_test 4.6.3 1 InvalidcAFalseTest3EE.crt basicConstraintsNotCriticalcAFalseCACert.crt +run_test 4.6.4 0 ValidbasicConstraintsNotCriticalTest4EE.crt basicConstraintsNotCriticalCACert.crt +run_test 4.6.5 1 InvalidpathLenConstraintTest5EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.6 1 InvalidpathLenConstraintTest6EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.7 0 ValidpathLenConstraintTest7EE.crt pathLenConstraint0CACert.crt +run_test 4.6.8 0 ValidpathLenConstraintTest8EE.crt pathLenConstraint0CACert.crt +run_test 4.6.9 1 InvalidpathLenConstraintTest9EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.10 1 InvalidpathLenConstraintTest10EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.11 1 InvalidpathLenConstraintTest11EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.12 1 InvalidpathLenConstraintTest12EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.13 0 ValidpathLenConstraintTest13EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.14 0 ValidpathLenConstraintTest14EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.15 0 ValidSelfIssuedpathLenConstraintTest15EE.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.16 1 InvalidSelfIssuedpathLenConstraintTest16EE.crt pathLenConstraint0subCA2Cert.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.17 0 ValidSelfIssuedpathLenConstraintTest17EE.crt pathLenConstraint1SelfIssuedsubCACert.crt pathLenConstraint1subCACert.crt pathLenConstraint1SelfIssuedCACert.crt pathLenConstraint1CACert.crt + +run_test 4.7.1 1 InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt keyUsageCriticalkeyCertSignFalseCACert.crt +run_test 4.7.2 1 InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt keyUsageNotCriticalkeyCertSignFalseCACert.crt +run_test 4.7.3 0 ValidkeyUsageNotCriticalTest3EE.crt keyUsageNotCriticalCACert.crt +run_test 4.7.4 1 InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt keyUsageCriticalcRLSignFalseCACert.crt +run_test 4.7.5 1 InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt keyUsageNotCriticalcRLSignFalseCACert.crt + +run_test 4.8.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt +run_test 4.8.2 0 AllCertificatesNoPoliciesTest2EE.crt NoPoliciesCACert.crt +run_test 4.8.3 0 DifferentPoliciesTest3EE.crt PoliciesP2subCACert.crt GoodCACert.crt +run_test 4.8.4 0 DifferentPoliciesTest4EE.crt GoodsubCACert.crt GoodCACert.crt +run_test 4.8.5 0 DifferentPoliciesTest5EE.crt PoliciesP2subCA2Cert.crt GoodCACert.crt +run_test 4.8.6 0 OverlappingPoliciesTest6EE.crt PoliciesP1234subsubCAP123P12Cert.crt PoliciesP1234subCAP123Cert.crt PoliciesP1234CACert.crt +run_test 4.8.7 0 DifferentPoliciesTest7EE.crt PoliciesP123subsubCAP12P1Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt +run_test 4.8.8 0 DifferentPoliciesTest8EE.crt PoliciesP12subsubCAP1P2Cert.crt PoliciesP12subCAP1Cert.crt PoliciesP12CACert.crt +run_test 4.8.9 0 DifferentPoliciesTest9EE.crt PoliciesP123subsubsubCAP12P2P1Cert.crt PoliciesP123subsubCAP12P2Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt +run_test 4.8.10 0 AllCertificatesSamePoliciesTest10EE.crt PoliciesP12CACert.crt +run_test 4.8.11 0 AllCertificatesanyPolicyTest11EE.crt anyPolicyCACert.crt +run_test 4.8.12 0 DifferentPoliciesTest12EE.crt PoliciesP3CACert.crt +run_test 4.8.13 0 AllCertificatesSamePoliciesTest13EE.crt PoliciesP123CACert.crt +run_test 4.8.14 0 AnyPolicyTest14EE.crt anyPolicyCACert.crt +run_test 4.8.15 0 UserNoticeQualifierTest15EE.crt +run_test 4.8.16 0 UserNoticeQualifierTest16EE.crt GoodCACert.crt +run_test 4.8.17 0 UserNoticeQualifierTest17EE.crt GoodCACert.crt +run_test 4.8.18 0 UserNoticeQualifierTest18EE.crt PoliciesP12CACert.crt +run_test 4.8.19 0 UserNoticeQualifierTest19EE.crt TrustAnchorRootCertificate.crt +run_test 4.8.20 0 CPSPointerQualifierTest20EE.crt GoodCACert.crt + +if false; then +# DSA tests +run_test 4.1.4 0 ValidDSASignaturesTest4EE.crt DSACACert.crt +fi + +popd + + +echo "Successful tests:$SUCCESS" +echo "Failed tests:$FAILURE" diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt new file mode 100644 index 0000000000000..a02a937e63dfb --- /dev/null +++ b/wpa_supplicant/todo.txt @@ -0,0 +1,92 @@ +To do: +- hostap: try other roaming modes + NOTE: current mode (manual roaming) does not really roam at all.. + Firmware did not notice the current AP disappearing.. +- add support for WPA with ap_scan=0 (update selected cipher etc. based on + AssocInfo; make sure these match with configuration) +- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA + authentication has been completed (cache scard data based on serial#(?) + and try to optimize next connection if the same card is present for next + auth) +- on disconnect event, could try to associate with another AP if one is + present in scan results; would need to update scan results periodically.. +- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from + ssid->proto fields to avoid detecting downgrade attacks when the driver + is not reporting RSN IE, but msg 3/4 has one +- Cisco AP and non-zero keyidx for unicast -> map to broadcast + (actually, this already works with driver_ndis; so maybe just change + driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx + for unicast); worked also with Host AP driver and madwifi +- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem + to see unencrypted EAPOL-Key frames at all.. +- EAP-PAX with PAX_SEC +- EAP (RFC 3748) + * OTP Extended Responses (Sect. 5.5) +- test what happens if authenticator sends EAP-Success before real EAP + authentication ("canned" Success); this should be ignored based on + RFC 3748 Sect. 4.2 +- test compilation with gcc -W options (more warnings?) + (Done once; number of unused function arguments still present) +- add proper support for using dot11RSNAConfigSATimeout +- ctrl_iface: get/set/remove blob +- use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and + web pages including the same information.. i.e., have this information only + in one page; how to build a PDF file with all the SGML included? +- EAP-POTP/RSA SecurID profile (RFC 4793) +- document wpa_gui build and consider adding it to 'make install' +- test madwifi with pairwise=TKIP group=WEP104 +- possibility to link in WPA Authenticator state machine to wpa_supplicant + (new PeerKey handshake, WPA2/IEEE 802.11 (RSN) IBSS) +- consider merging hostapd and wpa_supplicant PMKSA cache implementations +- consider redesigning pending EAP requests (identity/password/otp from + ctrl_iface) by moving the retrying of the previous request into EAP + state machine so that EAPOL state machine is not needed for this +- rfc4284.txt (network selection for eap) +- www pages about configuring wpa_supplicant: + * global options (ap_scan, ctrl_interfaces) based on OS/driver + * network block + * key_mgmt selection + * WPA parameters + * EAP options (one page for each method) + * "configuration wizard" (step 1: select OS, step 2: select driver, ...) to + generate example configuration +- error path in rsn_preauth_init: should probably deinit l2_packet handlers + if something fails; does something else need deinit? +- consider moving SIM card functionality (IMSI fetching) away from eap.c; + this should likely happen before EAP is initialized for authentication; + now IMSI is read only after receiving EAP-Identity/Request, but since it is + really needed for all cases, reading IMSI and generating Identity string + could very well be done before EAP has been started +- try to work around race in receiving association event and first EAPOL + message +- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to + clear memory; this would be used to clear temporary buffers containing + private data (e.g., keys); the macro can be defined to NOP in order to save + space (i.e., no code should depend on the macro doing something) +- make sure that TLS session cache is not shared between EAP types or if it + is, that the cache entries are bound to only one EAP type; e.g., cache entry + created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS +- consider moving eap_tls_build_ack() call into eap_tls_process_helper() + (it seems to be called always if helper returns 1) + * could need to modify eap_{ttls,peap,fast}_decrypt to do same +- add support for fetching full user cert chain from Windows certificate + stores even when there are intermediate CA certs that are not in the + configured ca_cert store (e.g., ROOT) (they could be, e.g., in CA store) + + +0.6.x branch: +- clean up common.[ch] +- change TLS/crypto library interface to use a structure of function + pointers and helper inline functions (like driver_ops) instead of + requiring every TLS wrapper to implement all functions +- add support for encrypted configuration fields (e.g., password, psk, + passphrase, pin) +- wpa_gui: add support for setting and showing priority, auth_alg + (open/shared for static WEP) + +- cleanup TLS/PEAP/TTLS/FAST fragmentation: both the handshake and Appl. Data + phases should be able to use the same functions for this; + the last step in processing sent should be this code and rest of the code + should not need to care about fragmentation at all +- test EAP-FAST peer with OpenSSL and verify that fallback to full handshake + (ServerHello followed by something else than ChangeCipherSpec) diff --git a/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj new file mode 100755 index 0000000000000..ffe12c46ad34a --- /dev/null +++ b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj @@ -0,0 +1,425 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="eapol_test" + ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}" + RootNamespace="eapol_test" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="2" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="1" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\src\crypto\aes_wrap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\base64.c" + > + </File> + <File + RelativePath="..\..\blacklist.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\chap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\common.c" + > + </File> + <File + RelativePath="..\..\config.c" + > + </File> + <File + RelativePath="..\..\config_file.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\crypto_openssl.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface_named_pipe.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_aka.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_gtc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_leap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_md5.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_methods.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_otp.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_peap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_peap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_sim.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_sim_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tnc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_ttls.c" + > + </File> + <File + RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c" + > + </File> + <File + RelativePath="..\..\eapol_test.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\eloop_win.c" + > + </File> + <File + RelativePath="..\..\events.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\ip_addr.c" + > + </File> + <File + RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md5.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\ms_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\os_win32.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\pcsc_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\peerkey.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\preauth.c" + > + </File> + <File + RelativePath="..\..\..\src\radius\radius.c" + > + </File> + <File + RelativePath="..\..\..\src\radius\radius_client.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\rc4.c" + > + </File> + <File + RelativePath="..\..\scan.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\scan_helpers.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\sha1.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\tls_openssl.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\tncc.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa.c" + > + </File> + <File + RelativePath="..\..\..\src\common\wpa_common.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpa_debug.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa_ie.c" + > + </File> + <File + RelativePath="..\..\wpa_supplicant.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpabuf.c" + > + </File> + <File + RelativePath="..\..\wpas_glue.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj b/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj new file mode 100755 index 0000000000000..89122f80cc794 --- /dev/null +++ b/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="win_if_list" + ProjectGUID="{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}" + RootNamespace="win_if_list" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wpcap.lib" + LinkIncremental="2" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wpcap.lib" + LinkIncremental="1" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\win_if_list.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj new file mode 100755 index 0000000000000..1cc31a6c487d1 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="wpa_cli" + ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}" + RootNamespace="wpa_cli" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\..\src\utils;..\..\..\src\common" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + DisableSpecificWarnings="4244;4267" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..\..\src\utils;..\..\..\src\common" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + DisableSpecificWarnings="4244;4267" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\src\utils\common.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\os_win32.c" + > + </File> + <File + RelativePath="..\..\wpa_cli.c" + > + </File> + <File + RelativePath="..\..\..\src\common\wpa_ctrl.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj new file mode 100755 index 0000000000000..2a9a590aebffa --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="wpa_passphrase" + ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}" + RootNamespace="wpa_passphrase" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\..\src\utils;..\..\..\src\crypto;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS;INTERNAL_SHA1;INTERNAL_MD5" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + DisableSpecificWarnings="4244;4267" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + LinkIncremental="2" + AdditionalLibraryDirectories="" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..\..\src\utils;..\..\..\src\crypto;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS;INTERNAL_SHA1;INTERNAL_MD5" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + DisableSpecificWarnings="4244;4267" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\src\utils\common.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md5.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\os_win32.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\sha1.c" + > + </File> + <File + RelativePath="..\..\wpa_passphrase.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/vs2005/wpa_supplicant.sln b/wpa_supplicant/vs2005/wpa_supplicant.sln new file mode 100755 index 0000000000000..97b756652dade --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_supplicant", "wpa_supplicant\wpa_supplicant.vcproj", "{8BCFDA77-AEDC-4168-8897-5B73105BBB87}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_cli", "wpa_cli\wpa_cli.vcproj", "{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpasvc", "wpasvc\wpasvc.vcproj", "{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_passphrase", "wpa_passphrase\wpa_passphrase.vcproj", "{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win_if_list", "win_if_list\win_if_list.vcproj", "{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eapol_test", "eapol_test\eapol_test.vcproj", "{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}" +EndProject +Global + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.ActiveCfg = Debug|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.Build.0 = Debug|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.ActiveCfg = Release|Win32 + {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.Build.0 = Release|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.Build.0 = Debug|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.ActiveCfg = Release|Win32 + {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.Build.0 = Release|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.ActiveCfg = Debug|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.Build.0 = Debug|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.ActiveCfg = Release|Win32 + {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.Build.0 = Release|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.Build.0 = Debug|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.ActiveCfg = Release|Win32 + {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.Build.0 = Release|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.ActiveCfg = Debug|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.Build.0 = Debug|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.ActiveCfg = Release|Win32 + {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.Build.0 = Release|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.Build.0 = Debug|Win32 + {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj new file mode 100755 index 0000000000000..2227f32de8180 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj @@ -0,0 +1,421 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="wpa_supplicant" + ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}" + RootNamespace="wpa_supplicant" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="2" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="1" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\src\crypto\aes_wrap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\base64.c" + > + </File> + <File + RelativePath="..\..\blacklist.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\chap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\common.c" + > + </File> + <File + RelativePath="..\..\config.c" + > + </File> + <File + RelativePath="..\..\config_file.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\crypto_openssl.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface_named_pipe.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\driver_ndis.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\driver_ndis_.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\drivers.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_gtc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_leap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_md5.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_methods.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_otp.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_peap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_peap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tnc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_ttls.c" + > + </File> + <File + RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\eloop_win.c" + > + </File> + <File + RelativePath="..\..\events.c" + > + </File> + <File + RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c" + > + </File> + <File + RelativePath="..\..\main.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md4.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md5.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\ms_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\ndis_events.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\os_win32.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\pcsc_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\peerkey.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\preauth.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\rc4.c" + > + </File> + <File + RelativePath="..\..\scan.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\scan_helpers.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\sha1.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\tls_openssl.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\tncc.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa.c" + > + </File> + <File + RelativePath="..\..\..\src\common\wpa_common.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpa_debug.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa_ie.c" + > + </File> + <File + RelativePath="..\..\wpa_supplicant.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpabuf.c" + > + </File> + <File + RelativePath="..\..\wpas_glue.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj new file mode 100755 index 0000000000000..676f1446c8ba2 --- /dev/null +++ b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj @@ -0,0 +1,421 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="wpasvc" + ProjectGUID="{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}" + RootNamespace="wpasvc" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="2" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="0" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;C:\dev\WpdPack\include;C:\dev\openssl\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + DisableSpecificWarnings="4244;4267;4311" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib" + LinkIncremental="1" + AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\src\crypto\aes_wrap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\base64.c" + > + </File> + <File + RelativePath="..\..\blacklist.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\chap.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\common.c" + > + </File> + <File + RelativePath="..\..\config.c" + > + </File> + <File + RelativePath="..\..\config_winreg.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\crypto_openssl.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface.c" + > + </File> + <File + RelativePath="..\..\ctrl_iface_named_pipe.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\driver_ndis.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\driver_ndis_.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\drivers.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_gtc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_leap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_md5.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_methods.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_otp.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_peap.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_common\eap_peap_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tls_common.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_tnc.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\eap_ttls.c" + > + </File> + <File + RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\eloop_win.c" + > + </File> + <File + RelativePath="..\..\events.c" + > + </File> + <File + RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c" + > + </File> + <File + RelativePath="..\..\main_winsvc.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md4.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\md5.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\ms_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\mschapv2.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\ndis_events.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\os_win32.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\pcsc_funcs.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\peerkey.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\preauth.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\rc4.c" + > + </File> + <File + RelativePath="..\..\scan.c" + > + </File> + <File + RelativePath="..\..\..\src\drivers\scan_helpers.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\sha1.c" + > + </File> + <File + RelativePath="..\..\..\src\crypto\tls_openssl.c" + > + </File> + <File + RelativePath="..\..\..\src\eap_peer\tncc.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa.c" + > + </File> + <File + RelativePath="..\..\..\src\common\wpa_common.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpa_debug.c" + > + </File> + <File + RelativePath="..\..\..\src\rsn_supp\wpa_ie.c" + > + </File> + <File + RelativePath="..\..\wpa_supplicant.c" + > + </File> + <File + RelativePath="..\..\..\src\utils\wpabuf.c" + > + </File> + <File + RelativePath="..\..\wpas_glue.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/wpa_supplicant/win_example.reg b/wpa_supplicant/win_example.reg new file mode 100755 index 0000000000000..6e42037e143c4 --- /dev/null +++ b/wpa_supplicant/win_example.reg @@ -0,0 +1,42 @@ +REGEDIT4 + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant] +"debug_level"=dword:00000000 +"debug_show_keys"=dword:00000001 +"debug_timestamp"=dword:00000000 +"debug_use_file"=dword:00000000 + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs] + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test] +"ap_scan"=dword:00000002 +"update_config"=dword:00000001 +"uuid"="12345678-9abc-def0-1234-56789abcdef0" +"device_name"="Wireless Client" +"manufacturer"="Company" +"model_name"="cmodel" +"serial_number"="12345" +"device_type"="1-0050F204-1" +"os_version"="01020300" + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\blobs] +"testblob"=hex:01,02,03,04,05 + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks] + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000] +"ssid"="\"example network\"" +"key_mgmt"="WPA-PSK" +"psk"="\"secret password\"" +"pairwise"="CCMP" +"group"="CCMP" +"proto"="WPA" + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces] + +[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces\0000] +"adapter"="{A7627643-C310-49E5-BD89-7E77709C04AB}" +"config"="test" +"ctrl_interface"="" +"skip_on_error"=dword:00000000 + diff --git a/wpa_supplicant/win_if_list.c b/wpa_supplicant/win_if_list.c new file mode 100644 index 0000000000000..0e1532ed71f18 --- /dev/null +++ b/wpa_supplicant/win_if_list.c @@ -0,0 +1,179 @@ +/* + * win_if_list - Display network interfaces with description (for Windows) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This small tool is for the Windows build to provide an easy way of fetching + * a list of available network interfaces. + */ + +#include "includes.h" +#include <stdio.h> +#ifdef CONFIG_USE_NDISUIO +#include <winsock2.h> +#include <ntddndis.h> +#else /* CONFIG_USE_NDISUIO */ +#include "pcap.h" +#include <winsock.h> +#endif /* CONFIG_USE_NDISUIO */ + +#ifdef CONFIG_USE_NDISUIO + +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK + +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISUIO_QUERY_BINDING \ + _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_BIND_WAIT \ + _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _NDISUIO_QUERY_BINDING +{ + ULONG BindingIndex; + ULONG DeviceNameOffset; + ULONG DeviceNameLength; + ULONG DeviceDescrOffset; + ULONG DeviceDescrLength; +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; + + +static HANDLE ndisuio_open(void) +{ + DWORD written; + HANDLE h; + + h = CreateFile(TEXT("\\\\.\\\\Ndisuio"), + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + INVALID_HANDLE_VALUE); + if (h == INVALID_HANDLE_VALUE) + return h; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0, + &written, NULL)) { + printf("IOCTL_NDISUIO_BIND_WAIT failed: %d", + (int) GetLastError()); + CloseHandle(h); + return INVALID_HANDLE_VALUE; + } +#endif /* _WIN32_WCE */ + + return h; +} + + +static void ndisuio_query_bindings(HANDLE ndisuio) +{ + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + + b = malloc(blen); + if (b == NULL) + return; + + for (i = 0; ; i++) { + memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, + (DWORD) blen, &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d", + error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + printf("ifname: %s\ndescription: %s\n\n", name, desc); + } + + free(b); +} + + +static void ndisuio_enum_bindings(void) +{ + HANDLE ndisuio = ndisuio_open(); + if (ndisuio == INVALID_HANDLE_VALUE) + return; + + ndisuio_query_bindings(ndisuio); + CloseHandle(ndisuio); +} + +#else /* CONFIG_USE_NDISUIO */ + +static void show_dev(pcap_if_t *dev) +{ + printf("ifname: %s\ndescription: %s\n\n", + dev->name, dev->description); +} + + +static void pcap_enum_devs(void) +{ + pcap_if_t *devs, *dev; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + fprintf(stderr, "Error - pcap_findalldevs: %s\n", err); + return; + } + + for (dev = devs; dev; dev = dev->next) { + show_dev(dev); + } + + pcap_freealldevs(devs); +} + +#endif /* CONFIG_USE_NDISUIO */ + + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_USE_NDISUIO + ndisuio_enum_bindings(); +#else /* CONFIG_USE_NDISUIO */ + pcap_enum_devs(); +#endif /* CONFIG_USE_NDISUIO */ + + return 0; +} diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c new file mode 100644 index 0000000000000..7de65348cb79c --- /dev/null +++ b/wpa_supplicant/wpa_cli.c @@ -0,0 +1,1921 @@ +/* + * WPA Supplicant - command line interface for wpa_supplicant daemon + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_CTRL_IFACE + +#ifdef CONFIG_CTRL_IFACE_UNIX +#include <dirent.h> +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_READLINE +#include <readline/readline.h> +#include <readline/history.h> +#endif /* CONFIG_READLINE */ + +#include "wpa_ctrl.h" +#include "common.h" +#include "version.h" + + +static const char *wpa_cli_version = +"wpa_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> and contributors"; + + +static const char *wpa_cli_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n"; + +static const char *wpa_cli_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +static struct wpa_ctrl *ctrl_conn; +static int wpa_cli_quit = 0; +static int wpa_cli_attached = 0; +static int wpa_cli_connected = 0; +static int wpa_cli_last_id = 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 int ping_interval = 5; + + +static void print_help(); + + +static void usage(void) +{ + printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] " + "[-a<action file>] \\\n" + " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] " + "[command..]\n" + " -h = help (show this usage text)\n" + " -v = shown version information\n" + " -a = run in daemon mode executing the action file based on " + "events from\n" + " wpa_supplicant\n" + " -B = run a daemon in the background\n" + " default path: /var/run/wpa_supplicant\n" + " default interface: first interface found in socket path\n"); + print_help(); +} + + +static struct wpa_ctrl * wpa_cli_open_connection(const char *ifname) +{ +#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE) + ctrl_conn = wpa_ctrl_open(ifname); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */ + char *cfile; + int flen, res; + + if (ifname == NULL) + return NULL; + + flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; + cfile = os_malloc(flen); + if (cfile == NULL) + return NULL; + res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); + if (res < 0 || res >= flen) { + os_free(cfile); + return NULL; + } + + ctrl_conn = wpa_ctrl_open(cfile); + os_free(cfile); + return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +static void wpa_cli_close_connection(void) +{ + if (ctrl_conn == NULL) + return; + + if (wpa_cli_attached) { + wpa_ctrl_detach(ctrl_conn); + wpa_cli_attached = 0; + } + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; +} + + +static void wpa_cli_msg_cb(char *msg, size_t len) +{ + printf("%s\n", msg); +} + + +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +{ + char buf[2048]; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to wpa_supplicant - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + if (print) { + buf[len] = '\0'; + printf("%s", buf); + } + return 0; +} + + +static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +{ + return _wpa_ctrl_command(ctrl, cmd, 1); +} + + +static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0; + return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS"); +} + + +static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PING"); +} + + +static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "MIB"); +} + + +static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + print_help(); + return 0; +} + + +static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); + return 0; +} + + +static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + wpa_cli_quit = 1; + return 0; +} + + +static void wpa_cli_show_variables(void) +{ + printf("set variables:\n" + " EAPOL::heldPeriod (EAPOL state machine held period, " + "in seconds)\n" + " EAPOL::authPeriod (EAPOL state machine authentication " + "period, in seconds)\n" + " EAPOL::startPeriod (EAPOL state machine start period, in " + "seconds)\n" + " EAPOL::maxStart (EAPOL state machine maximum start " + "attempts)\n"); + printf(" dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in " + "seconds)\n" + " dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication" + " threshold\n\tpercentage)\n" + " dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing " + "security\n\tassociation in seconds)\n"); +} + + +static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_variables(); + return 0; + } + + if (argc != 2) { + printf("Invalid SET command: needs two arguments (variable " + "name and value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGOFF"); +} + + +static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGON"); +} + + +static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REASSOCIATE"); +} + + +static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid PREAUTH command: needs one argument " + "(BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long PREAUTH command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid AP_SCAN command: needs one argument (ap_scan " + "value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long AP_SCAN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid STKSTART command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long STKSTART command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid FT_DS command: needs one argument " + "(Target AP MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long FT_DS command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + /* Any BSSID */ + return wpa_ctrl_command(ctrl, "WPS_PBC"); + } + + /* Specific BSSID */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PBC command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + printf("Invalid WPS_PIN command: need one or two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- PIN: optional, used only with devices that have no " + "display\n"); + return -1; + } + + if (argc == 1) { + /* Use dynamically generated PIN (returned as reply) */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); + } + + /* Use hardcoded PIN from a label */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 2) { + printf("Invalid WPS_REG command: need two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- AP PIN\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_REG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long LEVEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid IDENTITY command: needs two arguments " + "(network id and identity)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long IDENTITY command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long IDENTITY command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PASSWORD command: needs two arguments " + "(network id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSWORD command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSWORD command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid NEW_PASSWORD command: needs two arguments " + "(network id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long NEW_PASSWORD command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long NEW_PASSWORD command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PIN command: needs two arguments " + "(network id and pin)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PIN command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PIN command.\n"); + return -1; + } + pos += ret; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid OTP command: needs two arguments (network " + "id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long OTP command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long OTP command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PASSPHRASE command: needs two arguments " + "(network id and passphrase)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSPHRASE command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSPHRASE command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid BSSID command: needs two arguments (network " + "id and BSSID)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "BSSID"); + if (ret < 0 || ret >= end - pos) { + printf("Too long BSSID command.\n"); + return -1; + } + pos += ret; + for (i = 0; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long BSSID command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LIST_NETWORKS"); +} + + +static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid SELECT_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid ENABLE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid DISABLE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ADD_NETWORK"); +} + + +static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid REMOVE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static void wpa_cli_show_network_variables(void) +{ + printf("set_network variables:\n" + " ssid (network name, SSID)\n" + " psk (WPA passphrase or pre-shared key)\n" + " key_mgmt (key management protocol)\n" + " identity (EAP identity)\n" + " password (EAP password)\n" + " ...\n" + "\n" + "Note: Values are entered in the same format as the " + "configuration file is using,\n" + "i.e., strings values need to be inside double quotation " + "marks.\n" + "For example: set_network 1 ssid \"network name\"\n" + "\n" + "Please see wpa_supplicant.conf documentation for full list " + "of\navailable variables.\n"); +} + + +static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_network_variables(); + return 0; + } + + if (argc != 3) { + printf("Invalid SET_NETWORK command: needs three arguments\n" + "(network id, variable name, and value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SET_NETWORK command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_network_variables(); + return 0; + } + + if (argc != 2) { + printf("Invalid GET_NETWORK command: needs two arguments\n" + "(network id and variable name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long GET_NETWORK command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISCONNECT"); +} + + +static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RECONNECT"); +} + + +static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SAVE_CONFIG"); +} + + +static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SCAN"); +} + + +static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SCAN_RESULTS"); +} + + +static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid BSS command: need one argument (index or " + "BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[64]; + int res; + + if (argc < 1 || argc > 2) { + printf("Invalid GET_CAPABILITY command: need either one or " + "two arguments\n"); + return -1; + } + + if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) { + printf("Invalid GET_CAPABILITY command: second argument, " + "if any, must be 'strict'\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0], + (argc == 2) ? " strict" : ""); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) +{ + printf("Available interfaces:\n"); + return wpa_ctrl_command(ctrl, "INTERFACES"); +} + + +static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc < 1) { + wpa_cli_list_interfaces(ctrl); + return 0; + } + + wpa_cli_close_connection(); + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(argv[0]); + + if (wpa_cli_open_connection(ctrl_ifname)) { + printf("Connected to interface '%s.\n", ctrl_ifname); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + } + } else { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } + return 0; +} + + +static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RECONFIGURE"); +} + + +static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "TERMINATE"); +} + + +static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid INTERFACE_ADD command: needs at least one " + "argument (interface name)\n" + "All arguments: ifname confname driver ctrl_interface " + "driver_param bridge_name\n"); + return -1; + } + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param>TAB<bridge_name> + */ + res = os_snprintf(cmd, sizeof(cmd), + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + argv[0], + argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", + argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", + argc > 5 ? argv[5] : ""); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid INTERFACE_REMOVE command: needs one argument " + "(interface name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "INTERFACE_LIST"); +} + + +enum wpa_cli_cmd_flags { + cli_cmd_flag_none = 0x00, + cli_cmd_flag_sensitive = 0x01 +}; + +struct wpa_cli_cmd { + const char *cmd; + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + enum wpa_cli_cmd_flags flags; + const char *usage; +}; + +static struct wpa_cli_cmd wpa_cli_commands[] = { + { "status", wpa_cli_cmd_status, + cli_cmd_flag_none, + "[verbose] = get current WPA/EAPOL/EAP status" }, + { "ping", wpa_cli_cmd_ping, + cli_cmd_flag_none, + "= pings wpa_supplicant" }, + { "mib", wpa_cli_cmd_mib, + cli_cmd_flag_none, + "= get MIB variables (dot1x, dot11)" }, + { "help", wpa_cli_cmd_help, + cli_cmd_flag_none, + "= show this usage help" }, + { "interface", wpa_cli_cmd_interface, + cli_cmd_flag_none, + "[ifname] = show interfaces/select interface" }, + { "level", wpa_cli_cmd_level, + cli_cmd_flag_none, + "<debug level> = change debug level" }, + { "license", wpa_cli_cmd_license, + cli_cmd_flag_none, + "= show full wpa_cli license" }, + { "quit", wpa_cli_cmd_quit, + cli_cmd_flag_none, + "= exit wpa_cli" }, + { "set", wpa_cli_cmd_set, + cli_cmd_flag_none, + "= set variables (shows list of variables when run without " + "arguments)" }, + { "logon", wpa_cli_cmd_logon, + cli_cmd_flag_none, + "= IEEE 802.1X EAPOL state machine logon" }, + { "logoff", wpa_cli_cmd_logoff, + cli_cmd_flag_none, + "= IEEE 802.1X EAPOL state machine logoff" }, + { "pmksa", wpa_cli_cmd_pmksa, + cli_cmd_flag_none, + "= show PMKSA cache" }, + { "reassociate", wpa_cli_cmd_reassociate, + cli_cmd_flag_none, + "= force reassociation" }, + { "preauthenticate", wpa_cli_cmd_preauthenticate, + cli_cmd_flag_none, + "<BSSID> = force preauthentication" }, + { "identity", wpa_cli_cmd_identity, + cli_cmd_flag_none, + "<network id> <identity> = configure identity for an SSID" }, + { "password", wpa_cli_cmd_password, + cli_cmd_flag_sensitive, + "<network id> <password> = configure password for an SSID" }, + { "new_password", wpa_cli_cmd_new_password, + cli_cmd_flag_sensitive, + "<network id> <password> = change password for an SSID" }, + { "pin", wpa_cli_cmd_pin, + cli_cmd_flag_sensitive, + "<network id> <pin> = configure pin for an SSID" }, + { "otp", wpa_cli_cmd_otp, + cli_cmd_flag_sensitive, + "<network id> <password> = configure one-time-password for an SSID" + }, + { "passphrase", wpa_cli_cmd_passphrase, + cli_cmd_flag_sensitive, + "<network id> <passphrase> = configure private key passphrase\n" + " for an SSID" }, + { "bssid", wpa_cli_cmd_bssid, + cli_cmd_flag_none, + "<network id> <BSSID> = set preferred BSSID for an SSID" }, + { "list_networks", wpa_cli_cmd_list_networks, + cli_cmd_flag_none, + "= list configured networks" }, + { "select_network", wpa_cli_cmd_select_network, + cli_cmd_flag_none, + "<network id> = select a network (disable others)" }, + { "enable_network", wpa_cli_cmd_enable_network, + cli_cmd_flag_none, + "<network id> = enable a network" }, + { "disable_network", wpa_cli_cmd_disable_network, + cli_cmd_flag_none, + "<network id> = disable a network" }, + { "add_network", wpa_cli_cmd_add_network, + cli_cmd_flag_none, + "= add a network" }, + { "remove_network", wpa_cli_cmd_remove_network, + cli_cmd_flag_none, + "<network id> = remove a network" }, + { "set_network", wpa_cli_cmd_set_network, + cli_cmd_flag_sensitive, + "<network id> <variable> <value> = set network variables (shows\n" + " list of variables when run without arguments)" }, + { "get_network", wpa_cli_cmd_get_network, + cli_cmd_flag_none, + "<network id> <variable> = get network variables" }, + { "save_config", wpa_cli_cmd_save_config, + cli_cmd_flag_none, + "= save the current configuration" }, + { "disconnect", wpa_cli_cmd_disconnect, + cli_cmd_flag_none, + "= disconnect and wait for reassociate/reconnect command before\n" + " connecting" }, + { "reconnect", wpa_cli_cmd_reconnect, + cli_cmd_flag_none, + "= like reassociate, but only takes effect if already disconnected" + }, + { "scan", wpa_cli_cmd_scan, + cli_cmd_flag_none, + "= request new BSS scan" }, + { "scan_results", wpa_cli_cmd_scan_results, + cli_cmd_flag_none, + "= get latest scan results" }, + { "bss", wpa_cli_cmd_bss, + cli_cmd_flag_none, + "<<idx> | <bssid>> = get detailed scan result info" }, + { "get_capability", wpa_cli_cmd_get_capability, + cli_cmd_flag_none, + "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" }, + { "reconfigure", wpa_cli_cmd_reconfigure, + cli_cmd_flag_none, + "= force wpa_supplicant to re-read its configuration file" }, + { "terminate", wpa_cli_cmd_terminate, + cli_cmd_flag_none, + "= terminate wpa_supplicant" }, + { "interface_add", wpa_cli_cmd_interface_add, + cli_cmd_flag_none, + "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n" + " <bridge_name> = adds new interface, all parameters but <ifname>\n" + " are optional" }, + { "interface_remove", wpa_cli_cmd_interface_remove, + cli_cmd_flag_none, + "<ifname> = removes the interface" }, + { "interface_list", wpa_cli_cmd_interface_list, + cli_cmd_flag_none, + "= list available interfaces" }, + { "ap_scan", wpa_cli_cmd_ap_scan, + cli_cmd_flag_none, + "<value> = set ap_scan parameter" }, + { "stkstart", wpa_cli_cmd_stkstart, + cli_cmd_flag_none, + "<addr> = request STK negotiation with <addr>" }, + { "ft_ds", wpa_cli_cmd_ft_ds, + cli_cmd_flag_none, + "<addr> = request over-the-DS FT with <addr>" }, + { "wps_pbc", wpa_cli_cmd_wps_pbc, + cli_cmd_flag_none, + "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" }, + { "wps_pin", wpa_cli_cmd_wps_pin, + cli_cmd_flag_sensitive, + "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " + "hardcoded)" }, + { "wps_reg", wpa_cli_cmd_wps_reg, + cli_cmd_flag_sensitive, + "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" }, + { NULL, NULL, cli_cmd_flag_none, NULL } +}; + + +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) +{ + char c; + size_t n; + + printf("%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + printf("%c", c); + if (c == '\n') + printf("%s", pad); + } + printf("\n"); +} + + +static void print_help(void) +{ + int n; + printf("commands:\n"); + for (n = 0; wpa_cli_commands[n].cmd; n++) + print_cmd_help(&wpa_cli_commands[n], " "); +} + + +#ifdef CONFIG_READLINE +static int cmd_has_sensitive_data(const char *cmd) +{ + const char *c, *delim; + int n; + size_t len; + + delim = os_strchr(cmd, ' '); + if (delim) + len = delim - cmd; + else + len = os_strlen(cmd); + + for (n = 0; (c = wpa_cli_commands[n].cmd); n++) { + if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c)) + return (wpa_cli_commands[n].flags & + cli_cmd_flag_sensitive); + } + return 0; +} +#endif /* CONFIG_READLINE */ + + +static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + struct wpa_cli_cmd *cmd, *match = NULL; + int count; + int ret = 0; + + count = 0; + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0) + { + match = cmd; + if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { + /* we have an exact match */ + count = 1; + break; + } + count++; + } + cmd++; + } + + if (count > 1) { + printf("Ambiguous command '%s'; possible commands:", argv[0]); + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (os_strncasecmp(cmd->cmd, argv[0], + os_strlen(argv[0])) == 0) { + printf(" %s", cmd->cmd); + } + cmd++; + } + printf("\n"); + ret = 1; + } else if (count == 0) { + printf("Unknown command '%s'\n", argv[0]); + ret = 1; + } else { + ret = match->handler(ctrl, argc - 1, &argv[1]); + } + + return ret; +} + + +static int str_match(const char *a, const char *b) +{ + return os_strncmp(a, b, os_strlen(b)) == 0; +} + + +static int wpa_cli_exec(const char *program, const char *arg1, + const char *arg2) +{ + char *cmd; + size_t len; + int res; + int ret = 0; + + len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; + cmd = os_malloc(len); + if (cmd == NULL) + return -1; + res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); + if (res < 0 || (size_t) res >= len) { + os_free(cmd); + return -1; + } + cmd[len - 1] = '\0'; +#ifndef _WIN32_WCE + if (system(cmd) < 0) + ret = -1; +#endif /* _WIN32_WCE */ + os_free(cmd); + + return ret; +} + + +static void wpa_cli_action_process(const char *msg) +{ + const char *pos; + char *copy = NULL, *id, *pos2; + + pos = msg; + if (*pos == '<') { + /* skip priority */ + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_CONNECTED)) { + int new_id = -1; + os_unsetenv("WPA_ID"); + os_unsetenv("WPA_ID_STR"); + os_unsetenv("WPA_CTRL_DIR"); + + pos = os_strstr(pos, "[id="); + if (pos) + copy = os_strdup(pos + 4); + + if (copy) { + pos2 = id = copy; + while (*pos2 && *pos2 != ' ') + pos2++; + *pos2++ = '\0'; + new_id = atoi(id); + os_setenv("WPA_ID", id, 1); + while (*pos2 && *pos2 != '=') + pos2++; + if (*pos2 == '=') + pos2++; + id = pos2; + while (*pos2 && *pos2 != ']') + pos2++; + *pos2 = '\0'; + os_setenv("WPA_ID_STR", id, 1); + os_free(copy); + } + + os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1); + + if (!wpa_cli_connected || new_id != wpa_cli_last_id) { + wpa_cli_connected = 1; + wpa_cli_last_id = new_id; + wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); + } + } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { + if (wpa_cli_connected) { + wpa_cli_connected = 0; + wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); + } + } else if (str_match(pos, WPA_EVENT_TERMINATING)) { + printf("wpa_supplicant is terminating - stop monitoring\n"); + wpa_cli_quit = 1; + } +} + + +#ifndef CONFIG_ANSI_C_EXTRA +static void wpa_cli_action_cb(char *msg, size_t len) +{ + wpa_cli_action_process(msg); +} +#endif /* CONFIG_ANSI_C_EXTRA */ + + +static void wpa_cli_reconnect(void) +{ + wpa_cli_close_connection(); + ctrl_conn = wpa_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + printf("Connection to wpa_supplicant re-established\n"); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + } + } +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, + int action_monitor) +{ + int first = 1; + if (ctrl_conn == NULL) { + wpa_cli_reconnect(); + return; + } + while (wpa_ctrl_pending(ctrl) > 0) { + char buf[256]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; + if (action_monitor) + wpa_cli_action_process(buf); + else { + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } + } else { + printf("Could not read pending message.\n"); + break; + } + } + + if (wpa_ctrl_pending(ctrl) < 0) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_reconnect(); + } +} + + +#ifdef CONFIG_READLINE +static char * wpa_cli_cmd_gen(const char *text, int state) +{ + static int i, len; + const char *cmd; + + if (state == 0) { + i = 0; + len = os_strlen(text); + } + + while ((cmd = wpa_cli_commands[i].cmd)) { + i++; + if (os_strncasecmp(cmd, text, len) == 0) + return os_strdup(cmd); + } + + return NULL; +} + + +static char * wpa_cli_dummy_gen(const char *text, int state) +{ + return NULL; +} + + +static char ** wpa_cli_completion(const char *text, int start, int end) +{ + return rl_completion_matches(text, start == 0 ? + wpa_cli_cmd_gen : wpa_cli_dummy_gen); +} +#endif /* CONFIG_READLINE */ + + +static void wpa_cli_interactive(void) +{ +#define max_args 10 + char cmdbuf[256], *cmd, *argv[max_args], *pos; + int argc; +#ifdef CONFIG_READLINE + char *home, *hfile = NULL; +#endif /* CONFIG_READLINE */ + + printf("\nInteractive mode\n\n"); + +#ifdef CONFIG_READLINE + rl_attempted_completion_function = wpa_cli_completion; + home = getenv("HOME"); + if (home) { + const char *fname = ".wpa_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) { + int res; + res = os_snprintf(hfile, hfile_len, "%s/%s", home, + fname); + if (res >= 0 && res < hfile_len) { + hfile[hfile_len - 1] = '\0'; + read_history(hfile); + stifle_history(100); + } + } + } +#endif /* CONFIG_READLINE */ + + do { + wpa_cli_recv_pending(ctrl_conn, 0, 0); +#ifndef CONFIG_NATIVE_WINDOWS + alarm(ping_interval); +#endif /* CONFIG_NATIVE_WINDOWS */ +#ifdef CONFIG_READLINE + cmd = readline("> "); + if (cmd && *cmd) { + HIST_ENTRY *h; + while (next_history()) + ; + h = previous_history(); + if (h == NULL || os_strcmp(cmd, h->line) != 0) + add_history(cmd); + next_history(); + } +#else /* CONFIG_READLINE */ + printf("> "); + cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin); +#endif /* CONFIG_READLINE */ +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + if (cmd == NULL) + break; + wpa_cli_recv_pending(ctrl_conn, 0, 0); + pos = cmd; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + argc = 0; + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + if (argc) + wpa_request(ctrl_conn, argc, argv); + + if (cmd != cmdbuf) + os_free(cmd); + } while (!wpa_cli_quit); + +#ifdef CONFIG_READLINE + if (hfile) { + /* Save command history, excluding lines that may contain + * passwords. */ + HIST_ENTRY *h; + history_set_pos(0); + while ((h = current_history())) { + char *p = h->line; + while (*p == ' ' || *p == '\t') + p++; + if (cmd_has_sensitive_data(p)) { + h = remove_history(where_history()); + if (h) { + os_free(h->line); + os_free(h->data); + os_free(h); + } else + next_history(); + } else + next_history(); + } + write_history(hfile); + os_free(hfile); + } +#endif /* CONFIG_READLINE */ +} + + +static void wpa_cli_action(struct wpa_ctrl *ctrl) +{ +#ifdef CONFIG_ANSI_C_EXTRA + /* TODO: ANSI C version(?) */ + printf("Action processing not supported in ANSI C build.\n"); +#else /* CONFIG_ANSI_C_EXTRA */ + fd_set rfds; + int fd, res; + struct timeval tv; + char buf[256]; /* note: large enough to fit in unsolicited messages */ + size_t len; + + fd = wpa_ctrl_get_fd(ctrl); + + while (!wpa_cli_quit) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = ping_interval; + tv.tv_usec = 0; + res = select(fd + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + break; + } + + if (FD_ISSET(fd, &rfds)) + wpa_cli_recv_pending(ctrl, 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 || os_memcmp(buf, "PONG", 4) != 0) { + printf("wpa_supplicant did not reply to PING " + "command - exiting\n"); + break; + } + } + } +#endif /* CONFIG_ANSI_C_EXTRA */ +} + + +static void wpa_cli_cleanup(void) +{ + wpa_cli_close_connection(); + if (pid_file) + os_daemonize_terminate(pid_file); + + os_program_deinit(); +} + +static void wpa_cli_terminate(int sig) +{ + wpa_cli_cleanup(); + exit(0); +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void wpa_cli_alarm(int sig) +{ + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); + } + if (!ctrl_conn) + wpa_cli_reconnect(); + if (ctrl_conn) + wpa_cli_recv_pending(ctrl_conn, 1, 0); + alarm(ping_interval); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static char * wpa_cli_get_default_ifname(void) +{ + char *ifname = NULL; + +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + if (!dir) + return NULL; + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* + * Skip the file if it is not a socket. Also accept + * DT_UNKNOWN (0) in case the C library or underlying + * file system does not support d_type. + */ + if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", dent->d_name); + ifname = os_strdup(dent->d_name); + break; + } + closedir(dir); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + char buf[2048], *pos; + size_t len; + struct wpa_ctrl *ctrl; + int ret; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return NULL; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL); + if (ret >= 0) { + buf[len] = '\0'; + pos = os_strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ifname = os_strdup(buf); + } + wpa_ctrl_close(ctrl); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + return ifname; +} + + +int main(int argc, char *argv[]) +{ + int interactive; + int warning_displayed = 0; + int c; + int daemonize = 0; + int ret = 0; + const char *global = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + c = getopt(argc, argv, "a:Bg:G:hi:p:P:v"); + if (c < 0) + break; + switch (c) { + case 'a': + action_file = optarg; + break; + case 'B': + daemonize = 1; + break; + case 'g': + global = optarg; + break; + case 'G': + ping_interval = atoi(optarg); + break; + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", wpa_cli_version); + return 0; + case 'i': + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(optarg); + break; + case 'p': + ctrl_iface_dir = optarg; + break; + case 'P': + pid_file = optarg; + break; + default: + usage(); + return -1; + } + } + + interactive = (argc == optind) && (action_file == NULL); + + if (interactive) + printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + + if (global) { +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + ctrl_conn = wpa_ctrl_open(NULL); +#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + ctrl_conn = wpa_ctrl_open(global); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + if (ctrl_conn == NULL) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + } + + for (; !global;) { + if (ctrl_ifname == NULL) + ctrl_ifname = wpa_cli_get_default_ifname(); + ctrl_conn = wpa_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + if (warning_displayed) + printf("Connection established.\n"); + break; + } + + if (!interactive) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant - " + "re-trying\n"); + warning_displayed = 1; + } + os_sleep(1, 0); + continue; + } + +#ifndef _WIN32_WCE + signal(SIGINT, wpa_cli_terminate); + signal(SIGTERM, wpa_cli_terminate); +#endif /* _WIN32_WCE */ +#ifndef CONFIG_NATIVE_WINDOWS + signal(SIGALRM, wpa_cli_alarm); +#endif /* CONFIG_NATIVE_WINDOWS */ + + 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 && os_daemonize(pid_file)) + return -1; + + if (interactive) + wpa_cli_interactive(); + else if (action_file) + wpa_cli_action(ctrl_conn); + else + ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); + + os_free(ctrl_ifname); + wpa_cli_cleanup(); + + return ret; +} + +#else /* CONFIG_CTRL_IFACE */ +int main(int argc, char *argv[]) +{ + printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n"); + return -1; +} +#endif /* CONFIG_CTRL_IFACE */ diff --git a/wpa_supplicant/wpa_gui-qt4/.gitignore b/wpa_supplicant/wpa_gui-qt4/.gitignore new file mode 100644 index 0000000000000..efcb6660002e5 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/.gitignore @@ -0,0 +1,6 @@ +.moc +.obj +.ui +Makefile +wpa_gui +qrc_icons.cpp diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp new file mode 100644 index 0000000000000..02fecfea42cd7 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp @@ -0,0 +1,245 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> +#include "wpa_ctrl.h" + +#include <QMessageBox> + +#include "wpagui.h" +#include "addinterface.h" + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif +#endif /* CONFIG_NATIVE_WINDOWS */ + + +AddInterface::AddInterface(WpaGui *_wpagui, QWidget *parent) + : QDialog(parent), wpagui(_wpagui) +{ + setWindowTitle("Select network interface to add"); + resize(400, 200); + vboxLayout = new QVBoxLayout(this); + + interfaceWidget = new QTreeWidget(this); + interfaceWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + interfaceWidget->setUniformRowHeights(true); + interfaceWidget->setSortingEnabled(true); + interfaceWidget->setColumnCount(3); + interfaceWidget->headerItem()->setText(0, "driver"); + interfaceWidget->headerItem()->setText(1, "interface"); + interfaceWidget->headerItem()->setText(2, "description"); + interfaceWidget->setItemsExpandable(FALSE); + interfaceWidget->setRootIsDecorated(FALSE); + vboxLayout->addWidget(interfaceWidget); + + connect(interfaceWidget, + SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, + SLOT(interfaceSelected(QTreeWidgetItem *))); + + addInterfaces(); +} + + +void AddInterface::addInterfaces() +{ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[2048]; + size_t len; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACE_LIST", 14, buf, &len, NULL); + if (ret < 0) { + wpa_ctrl_close(ctrl); + return; + } + buf[len] = '\0'; + + wpa_ctrl_close(ctrl); + + QString ifaces(buf); + QStringList lines = ifaces.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + QStringList arg = (*it).split(QChar('\t')); + if (arg.size() < 3) + continue; + QTreeWidgetItem *item = new QTreeWidgetItem(interfaceWidget); + if (!item) + break; + + item->setText(0, arg[0]); + item->setText(1, arg[1]); + item->setText(2, arg[2]); + } + + interfaceWidget->resizeColumnToContents(0); + interfaceWidget->resizeColumnToContents(1); + interfaceWidget->resizeColumnToContents(2); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +#ifdef CONFIG_NATIVE_WINDOWS +bool AddInterface::addRegistryInterface(const QString &ifname) +{ + HKEY hk, ihk; + LONG ret; + int id, tmp; + TCHAR name[10]; + DWORD val, i; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX TEXT("\\interfaces"), + 0, KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY, + &hk); + if (ret != ERROR_SUCCESS) + return false; + + id = -1; + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) + break; + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + +#ifdef UNICODE + QString s((QChar *) name, namelen); +#else /* UNICODE */ + QString s(name); +#endif /* UNICODE */ + tmp = s.toInt(); + if (tmp > id) + id = tmp; + } + + id += 1; + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(hk, name, 0, NULL, 0, KEY_WRITE, NULL, &ihk, + NULL); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) + return false; + +#ifdef UNICODE + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.unicode(), + (ifname.length() + 1) * sizeof(TCHAR)); + +#else /* UNICODE */ + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.toLocal8Bit(), ifname.length() + 1); +#endif /* UNICODE */ + RegSetValueEx(ihk, TEXT("config"), 0, REG_SZ, + (LPBYTE) TEXT("default"), 8 * sizeof(TCHAR)); + RegSetValueEx(ihk, TEXT("ctrl_interface"), 0, REG_SZ, + (LPBYTE) TEXT(""), 1 * sizeof(TCHAR)); + val = 1; + RegSetValueEx(ihk, TEXT("skip_on_error"), 0, REG_DWORD, (LPBYTE) &val, + sizeof(val)); + + RegCloseKey(ihk); + return true; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void AddInterface::interfaceSelected(QTreeWidgetItem *sel) +{ + if (!sel) + return; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[20], cmd[256]; + size_t len; + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param>TAB<bridge_name> + */ + snprintf(cmd, sizeof(cmd), + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + sel->text(1).toAscii().constData(), + "default", + sel->text(0).toAscii().constData(), + "yes", "", ""); + cmd[sizeof(cmd) - 1] = '\0'; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL); + wpa_ctrl_close(ctrl); + + if (ret < 0) { + QMessageBox::warning(this, "wpa_gui", + "Add interface command could not be " + "completed."); + return; + } + + buf[len] = '\0'; + if (buf[0] != 'O' || buf[1] != 'K') { + QMessageBox::warning(this, "wpa_gui", + "Failed to add the interface."); + return; + } + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#ifdef CONFIG_NATIVE_WINDOWS + if (!addRegistryInterface(sel->text(1))) { + QMessageBox::information(this, "wpa_gui", + "Failed to add the interface into " + "registry."); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + wpagui->selectAdapter(sel->text(1)); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.h b/wpa_supplicant/wpa_gui-qt4/addinterface.h new file mode 100644 index 0000000000000..9d9476a46d99d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.h @@ -0,0 +1,45 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef ADDINTERFACE_H +#define ADDINTERFACE_H + +#include <QObject> + +#include <QtGui/QDialog> +#include <QtGui/QTreeWidget> +#include <QtGui/QVBoxLayout> + +class WpaGui; + +class AddInterface : public QDialog +{ + Q_OBJECT + +public: + AddInterface(WpaGui *_wpagui, QWidget *parent = 0); + +public slots: + virtual void interfaceSelected(QTreeWidgetItem *sel); + +private: + void addInterfaces(); + bool addRegistryInterface(const QString &ifname); + + QVBoxLayout *vboxLayout; + QTreeWidget *interfaceWidget; + WpaGui *wpagui; +}; + +#endif /* ADDINTERFACE_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp new file mode 100644 index 0000000000000..46deb96a53d9e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp @@ -0,0 +1,130 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <QHeaderView> +#include <QScrollBar> + +#include "eventhistory.h" + + +int EventListModel::rowCount(const QModelIndex &) const +{ + return msgList.count(); +} + + +int EventListModel::columnCount(const QModelIndex &) const +{ + return 2; +} + + +QVariant EventListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) + if (index.column() == 0) { + if (index.row() >= timeList.size()) + return QVariant(); + return timeList.at(index.row()); + } else { + if (index.row() >= msgList.size()) + return QVariant(); + return msgList.at(index.row()); + } + else + return QVariant(); +} + + +QVariant EventListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString("Timestamp"); + case 1: + return QString("Message"); + default: + return QVariant(); + } + } else + return QString("%1").arg(section); +} + + +void EventListModel::addEvent(QString time, QString msg) +{ + beginInsertRows(QModelIndex(), msgList.size(), msgList.size() + 1); + timeList << time; + msgList << msg; + endInsertRows(); +} + + +EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + eventListView->setItemsExpandable(FALSE); + eventListView->setRootIsDecorated(FALSE); + elm = new EventListModel(parent); + eventListView->setModel(elm); +} + + +EventHistory::~EventHistory() +{ + destroy(); + delete elm; +} + + +void EventHistory::languageChange() +{ + retranslateUi(this); +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) + addEvent(*it); +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + bool scroll = true; + + if (eventListView->verticalScrollBar()->value() < + eventListView->verticalScrollBar()->maximum()) + scroll = false; + + elm->addEvent(msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + + if (scroll) + eventListView->scrollToBottom(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h new file mode 100644 index 0000000000000..40dff6d6d7793 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h @@ -0,0 +1,63 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EVENTHISTORY_H +#define EVENTHISTORY_H + +#include <QObject> +#include "ui_eventhistory.h" + + +class EventListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + EventListModel(QObject *parent = 0) + : QAbstractTableModel(parent) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + void addEvent(QString time, QString msg); + +private: + QStringList timeList; + QStringList msgList; +}; + + +class EventHistory : public QDialog, public Ui::EventHistory +{ + Q_OBJECT + +public: + EventHistory(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~EventHistory(); + +public slots: + virtual void addEvents(WpaMsgList msgs); + virtual void addEvent(WpaMsg msg); + +protected slots: + virtual void languageChange(); + +private: + EventListModel *elm; +}; + +#endif /* EVENTHISTORY_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui new file mode 100644 index 0000000000000..afe9149cfa0fd --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui @@ -0,0 +1,61 @@ +<ui version="4.0" > + <class>EventHistory</class> + <widget class="QDialog" name="EventHistory" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="windowTitle" > + <string>Event history</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QTreeView" name="eventListView" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="verticalScrollBarPolicy" > + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::NoSelection</enum> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="local" >wpamsg.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/icons.qrc b/wpa_supplicant/wpa_gui-qt4/icons.qrc new file mode 100644 index 0000000000000..93e94fce24cca --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.svg">icons/wpa_gui.svg</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile new file mode 100644 index 0000000000000..cb5c65e701f42 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile @@ -0,0 +1,27 @@ +#!/usr/bin/make -f + +NAME := wpa_gui +SVG := $(NAME).svg +SIZES := 16x16 22x22 32x32 48x48 64x64 128x128 +ICONS := $(addsuffix .png,$(SIZES)) +ICONS += $(addsuffix .xpm,$(NAME) $(NAME)-16) + +all: $(ICONS) + +%.png: + mkdir -p hicolor/$(@:.png=)/apps/ + inkscape $(SVG) --without-gui \ + --export-width=$(word 1,$(subst x, ,$(@:.png=))) \ + --export-height=$(word 2,$(subst x, ,$(@:.png=))) \ + --export-png=hicolor/$(@:.png=)/apps/$(NAME).png + +$(NAME).xpm: + mkdir -p pixmaps/ + convert hicolor/32x32/apps/$(NAME).png pixmaps/$@ + +$(NAME)-16.xpm: + mkdir -p pixmaps/ + convert hicolor/16x16/apps/$(NAME).png pixmaps/$@ + +clean: + $(RM) -r pixmaps hicolor diff --git a/wpa_supplicant/wpa_gui-qt4/icons/README b/wpa_supplicant/wpa_gui-qt4/icons/README new file mode 100644 index 0000000000000..1584eb51d62e1 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/README @@ -0,0 +1,7 @@ +Copyright (c) 2008 Bernard Gray <bernard.gray@gmail.com> + +The wpa_gui icon is licensed under the GPL version 2. Alternatively, the icon +may be distributed under the terms of BSD license. + +To convert the svg icon to other formats, make sure inkscape and imagemagick +are installed and use `make' to create various sized png and xpm icons. diff --git a/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg new file mode 100644 index 0000000000000..b3abf0a288d89 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="128" + height="128" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="wpa_gui.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata47"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="771" + inkscape:window-width="640" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="4.2421875" + inkscape:cx="64" + inkscape:cy="64" + inkscape:window-x="634" + inkscape:window-y="0" + inkscape:current-layer="svg2" /> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 64 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="128 : 64 : 1" + inkscape:persp3d-origin="64 : 42.666667 : 1" + id="perspective49" /> + <linearGradient + id="linearGradient39133"> + <stop + id="stop39135" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39137" + style="stop-color:#515151;stop-opacity:1" + offset="0" /> + <stop + id="stop39139" + style="stop-color:#878787;stop-opacity:1" + offset="0.28677997" /> + <stop + id="stop39141" + style="stop-color:#000000;stop-opacity:1" + offset="0.92151743" /> + <stop + id="stop39143" + style="stop-color:#ffffff;stop-opacity:0.73786408" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39119"> + <stop + id="stop39121" + style="stop-color:#ffffff;stop-opacity:0.82905984" + offset="0" /> + <stop + id="stop39123" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39106"> + <stop + id="stop39108" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop39110" + style="stop-color:#a8a8a8;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39094"> + <stop + id="stop39096" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop39098" + style="stop-color:#333333;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39062"> + <stop + id="stop39064" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39086" + style="stop-color:#515151;stop-opacity:1" + offset="0.21101321" /> + <stop + id="stop39088" + style="stop-color:#878787;stop-opacity:1" + offset="0.75" /> + <stop + id="stop39090" + style="stop-color:#6c6c6c;stop-opacity:1" + offset="0.875" /> + <stop + id="stop39066" + style="stop-color:#1e1e1e;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="4" + y1="40" + x2="124" + y2="60" + id="linearGradient39068" + xlink:href="#linearGradient39062" + gradientUnits="userSpaceOnUse" /> + <radialGradient + cx="100.70589" + cy="96" + r="60" + fx="158.07428" + fy="95.718063" + id="radialGradient39100" + xlink:href="#linearGradient39094" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.7837903e-8,-1,0.99999999,-2.1864248e-6,-32.000004,164.7061)" /> + <radialGradient + cx="100.44444" + cy="34.363636" + r="32" + fx="83.18" + fy="34.228985" + id="radialGradient39104" + xlink:href="#linearGradient39106" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.1472435e-6,1.0227273,-0.87499999,-9.5061964e-8,94.067865,-4.7272712)" /> + <radialGradient + cx="75.999977" + cy="-2.7730541" + r="48" + fx="55.266491" + fy="-2.5338216" + id="radialGradient39125" + xlink:href="#linearGradient39119" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,0.83333324,-1.6666667,2.518705e-6,59.378243,-35.333302)" /> + <radialGradient + cx="64.066589" + cy="63.713329" + r="60" + fx="64.066589" + fy="63.713329" + id="radialGradient39131" + xlink:href="#linearGradient39133" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1333333,5.1768857e-8,5.2556881e-6,1.1666667,-8.6091298,-10.332226)" /> + <filter + id="filter39153"> + <feGaussianBlur + id="feGaussianBlur39155" + stdDeviation="2.28" + inkscape:collect="always" /> + </filter> + <filter + id="filter39159"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.68" + id="feGaussianBlur39161" /> + </filter> + </defs> + <g + id="layer1" + style="display:inline"> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39151" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39153)" /> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39157" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39159)" /> + <rect + width="120" + height="120" + ry="25.00531" + x="4" + y="0" + id="rect2573" + style="opacity:1;fill:url(#radialGradient39100);fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.14706 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.14706 112.85294,0 99,0 L 29,0 z" + id="path39127" + style="opacity:0.20512821;fill:url(#radialGradient39131);fill-opacity:1;stroke:none" /> + <path + d="m 44,68 40,0 12,40 c -20,7.27273 -44,7.27273 -64,0 L 44,68 z" + id="path39102" + style="opacity:0.53418801;fill:url(#radialGradient39104);fill-opacity:1;stroke:none" /> + <path + d="M 25.339207,12 C 52,8 76,8 102.66079,12 107.83471,12 112,16.165286 112,21.339207 L 116,52 C 100,73.339207 28,73.339207 12,52 L 16,21.339207 C 16,16.165286 20.165286,12 25.339207,12 z" + id="rect39116" + style="opacity:0.92307691;fill:url(#radialGradient39125);fill-opacity:1;stroke:none" /> + <path + d="M 29,8 C 15.147058,8 4,19.14706 4,33 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,19.14706 112.85294,8 99,8 L 29,8 z" + id="path39147" + style="opacity:0.20512821;fill:#000000;fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.147058 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.147058 112.85294,0 99,0 L 29,0 z m 0,4 70,0 c 11.70613,0 21,9.293869 21,21 l 0,70 c 0,11.70613 -9.29387,21 -21,21 l -70,0 C 17.293869,116 8,106.70613 8,95 L 8,25 C 8,13.293869 17.293869,4 29,4 z" + id="rect39029" + style="opacity:1;fill:url(#linearGradient39068);fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.16680323,0.53082142,-0.53082142,-0.16680323,103.31027,53.117897)" + id="path3351" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:21.56673813;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <path + d="m 36,56 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z" + transform="matrix(1.4851301,0,0,1.4851301,16.475837,-23.948973)" + id="path3353" + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.35033273,1.1148712,-1.1148712,-0.35033273,146.5624,46.88078)" + id="path2622" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:10.26852894;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc new file mode 100644 index 0000000000000..09f3d962008b1 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp new file mode 100644 index 0000000000000..9585e1ef0b644 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/main.cpp @@ -0,0 +1,66 @@ +/* + * wpa_gui - Application startup + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <QApplication> +#include "wpagui.h" + + +class WpaGuiApp : public QApplication +{ +public: + WpaGuiApp(int &argc, char **argv); + + virtual void saveState(QSessionManager &manager); + + WpaGui *w; +}; + +WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv) +{ +} + +void WpaGuiApp::saveState(QSessionManager &manager) +{ + QApplication::saveState(manager); + w->saveState(); +} + + +int main(int argc, char *argv[]) +{ + WpaGuiApp app(argc, argv); + WpaGui w(&app); + 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 */ + + app.w = &w; + + ret = app.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp new file mode 100644 index 0000000000000..dae9edd38d39a --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp @@ -0,0 +1,823 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> +#include <QMessageBox> + +#include "networkconfig.h" +#include "wpagui.h" + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + + +NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(authSelect, SIGNAL(activated(int)), this, + SLOT(authChanged(int))); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); + connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(encrSelect, SIGNAL(activated(const QString &)), this, + SLOT(encrChanged(const QString &))); + connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork())); + connect(eapSelect, SIGNAL(activated(int)), this, + SLOT(eapChanged(int))); + connect(useWpsButton, SIGNAL(clicked()), this, SLOT(useWps())); + + wpagui = NULL; + new_network = false; +} + + +NetworkConfig::~NetworkConfig() +{ +} + + +void NetworkConfig::languageChange() +{ + retranslateUi(this); +} + + +void NetworkConfig::paramsFromScanResults(QTreeWidgetItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setWindowTitle(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.indexOf("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.indexOf("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.indexOf("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.indexOf("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE; + + if (flags.indexOf("-CCMP") >= 0) + encr = 1; + else if (flags.indexOf("-TKIP") >= 0) + encr = 0; + else if (flags.indexOf("WEP") >= 0) + encr = 1; + else + encr = 0; + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + + wepEnabled(auth == AUTH_NONE && encr == 1); + + getEapCapa(); + + if (flags.indexOf("[WPS") >= 0) + useWpsButton->setEnabled(true); + bssid = sel->text(1); +} + + +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); + phase2Select->setEnabled(eap); + if (eap) + eapChanged(eapSelect->currentIndex()); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) { + encrSelect->addItem("None"); + encrSelect->addItem("WEP"); + encrSelect->setCurrentIndex(sel == AUTH_NONE ? 0 : 1); + } else { + encrSelect->addItem("TKIP"); + encrSelect->addItem("CCMP"); + encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_IEEE8021X); +} + + +void NetworkConfig::eapChanged(int sel) +{ + QString prev_val = phase2Select->currentText(); + while (phase2Select->count()) + phase2Select->removeItem(0); + + QStringList inner; + inner << "PEAP" << "TTLS" << "FAST"; + if (!inner.contains(eapSelect->itemText(sel))) + return; + + phase2Select->addItem("[ any ]"); + + /* Add special cases based on outer method */ + if (eapSelect->currentText().compare("TTLS") == 0) { + phase2Select->addItem("PAP"); + phase2Select->addItem("CHAP"); + phase2Select->addItem("MSCHAP"); + phase2Select->addItem("MSCHAPv2"); + } else if (eapSelect->currentText().compare("FAST") == 0) + phase2Select->addItem("GTC(auth) + MSCHAPv2(prov)"); + + /* Add all enabled EAP methods that can be used in the tunnel */ + int i; + QStringList allowed; + allowed << "MSCHAPV2" << "MD5" << "GTC" << "TLS" << "OTP" << "SIM" + << "AKA"; + for (i = 0; i < eapSelect->count(); i++) { + if (allowed.contains(eapSelect->itemText(i))) { + phase2Select->addItem("EAP-" + eapSelect->itemText(i)); + } + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(prev_val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentIndex(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning(this, "WPA Pre-Shared Key Error", + "WPA-PSK requires a passphrase " + "of 8 to 63 characters\n" + "or 64 hex digit PSK"); + pskEdit->setFocus(); + return; + } + } + + if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) { + QRegExp rx("^(\\w|-)+$"); + if (rx.indexIn(idstrEdit->text()) < 0) { + QMessageBox::warning(this, "Network ID Error", + "Network ID String contains " + "non-word characters.\n" + "It must be a simple string, " + "without spaces, containing\n" + "only characters in this range: " + "[A-Za-z0-9_-]\n"); + idstrEdit->setFocus(); + return; + } + } + + if (wpagui == NULL) + return; + + memset(reply, 0, sizeof(reply)); + reply_len = sizeof(reply) - 1; + + if (new_network) { + wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); + if (reply[0] == 'F') { + QMessageBox::warning(this, "wpa_gui", "Failed to add " + "network to wpa_supplicant\n" + "configuration."); + return; + } + id = atoi(reply); + } else + id = edit_network_id; + + setNetworkParam(id, "ssid", ssidEdit->text().toAscii().constData(), + true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE: + 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->currentIndex(); + if (encr == 0) + pairwise = "TKIP"; + else + pairwise = "CCMP"; + } + + if (proto) + setNetworkParam(id, "proto", proto, false); + if (key_mgmt) + setNetworkParam(id, "key_mgmt", key_mgmt, false); + if (pairwise) { + setNetworkParam(id, "pairwise", pairwise, false); + setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); + } + if (pskEdit->isEnabled() && + strcmp(pskEdit->text().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", + pskEdit->text().toAscii().constData(), + psklen != 64); + if (eapSelect->isEnabled()) { + const char *eap = + eapSelect->currentText().toAscii().constData(); + setNetworkParam(id, "eap", eap, false); + if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0) + setNetworkParam(id, "pcsc", "", true); + else + setNetworkParam(id, "pcsc", "NULL", false); + } + if (phase2Select->isEnabled()) { + QString eap = eapSelect->currentText(); + QString inner = phase2Select->currentText(); + char phase2[32]; + phase2[0] = '\0'; + if (eap.compare("PEAP") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + } else if (eap.compare("TTLS") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "autheap=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + else + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.toAscii().constData()); + } else if (eap.compare("FAST") == 0) { + const char *provisioning = NULL; + if (inner.startsWith("EAP-")) { + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + provisioning = "fast_provisioning=2"; + } else if (inner.compare("GTC(auth) + MSCHAPv2(prov)") + == 0) { + snprintf(phase2, sizeof(phase2), + "auth=GTC auth=MSCHAPV2"); + provisioning = "fast_provisioning=1"; + } else + provisioning = "fast_provisioning=3"; + if (provisioning) { + char blob[32]; + setNetworkParam(id, "phase1", provisioning, + true); + snprintf(blob, sizeof(blob), + "blob://fast-pac-%d", id); + setNetworkParam(id, "pac_file", blob, true); + } + } + if (phase2[0]) + setNetworkParam(id, "phase2", phase2, true); + else + setNetworkParam(id, "phase2", "NULL", false); + } else + setNetworkParam(id, "phase2", "NULL", false); + if (identityEdit->isEnabled() && identityEdit->text().length() > 0) + setNetworkParam(id, "identity", + identityEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "identity", "NULL", false); + if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 && + strcmp(passwordEdit->text().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", + passwordEdit->text().toAscii().constData(), + true); + else if (passwordEdit->text().length() == 0) + setNetworkParam(id, "password", "NULL", false); + if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0) + setNetworkParam(id, "ca_cert", + cacertEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "ca_cert", "NULL", false); + writeWepKey(id, wep0Edit, 0); + writeWepKey(id, wep1Edit, 1); + writeWepKey(id, wep2Edit, 2); + writeWepKey(id, wep3Edit, 3); + + if (wep0Radio->isEnabled() && wep0Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "0", false); + else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "1", false); + else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "2", false); + else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "3", false); + + if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0) + setNetworkParam(id, "id_str", + idstrEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "id_str", "NULL", false); + + if (prioritySpinBox->isEnabled()) { + QString prio; + prio = prio.setNum(prioritySpinBox->value()); + setNetworkParam(id, "priority", prio.toAscii().constData(), + false); + } + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", "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.indexOf("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().toAscii().constData(); + if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) + return; + len = strlen(txt); + if (len == 0) + return; + pos = txt; + hex = true; + while (*pos) { + if (!((*pos >= '0' && *pos <= '9') || + (*pos >= 'a' && *pos <= 'f') || + (*pos >= 'A' && *pos <= 'F'))) { + hex = false; + break; + } + pos++; + } + if (hex && len != 10 && len != 26 && len != 32) + hex = false; + snprintf(buf, sizeof(buf), "wep_key%d", id); + setNetworkParam(network_id, buf, txt, !hex); +} + + +static int key_value_isset(const char *reply, size_t reply_len) +{ + return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); +} + + +void NetworkConfig::paramsFromConfig(int network_id) +{ + int i, res; + + edit_network_id = network_id; + getEapCapa(); + + char reply[1024], cmd[256], *pos; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + ssidEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); + reply_len = sizeof(reply) - 1; + int wpa = 0; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "RSN") || strstr(reply, "WPA2")) + wpa = 2; + else if (strstr(reply, "WPA")) + wpa = 1; + } + + int auth = AUTH_NONE, encr = 0; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "WPA-EAP")) + auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; + else if (strstr(reply, "WPA-PSK")) + auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; + else if (strstr(reply, "IEEE8021X")) { + auth = AUTH_IEEE8021X; + encr = 1; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP") && auth != AUTH_NONE) + encr = 1; + else if (strstr(reply, "TKIP")) + encr = 0; + else if (strstr(reply, "WEP")) + encr = 1; + else + encr = 0; + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + pskEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + pskEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + identityEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + passwordEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + passwordEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + cacertEdit->setText(reply + 1); + } + + enum { NO_INNER, PEAP_INNER, TTLS_INNER, FAST_INNER } eap = NO_INNER; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + for (i = 0; i < eapSelect->count(); i++) { + if (eapSelect->itemText(i).compare(reply) == 0) { + eapSelect->setCurrentIndex(i); + if (strcmp(reply, "PEAP") == 0) + eap = PEAP_INNER; + else if (strcmp(reply, "TTLS") == 0) + eap = TTLS_INNER; + else if (strcmp(reply, "FAST") == 0) + eap = FAST_INNER; + break; + } + } + } + + if (eap != NO_INNER) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d phase2", + network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + eapChanged(eapSelect->currentIndex()); + } else + eap = NO_INNER; + } + + char *val; + val = reply + 1; + while (*(val + 1)) + val++; + if (*val == '"') + *val = '\0'; + + switch (eap) { + case PEAP_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case TTLS_INNER: + if (strncmp(reply, "\"autheap=", 9) == 0) { + val = reply + 5; + memcpy(val, "EAP-", 4); + } else if (strncmp(reply, "\"auth=", 6) == 0) + val = reply + 6; + break; + case FAST_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + if (strcmp(reply + 6, "GTC auth=MSCHAPV2") == 0) { + val = (char *) "GTC(auth) + MSCHAPv2(prov)"; + break; + } + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case NO_INNER: + break; + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } + + for (i = 0; i < 4; i++) { + QLineEdit *wepEdit; + switch (i) { + default: + case 0: + wepEdit = wep0Edit; + break; + case 1: + wepEdit = wep1Edit; + break; + case 2: + wepEdit = wep2Edit; + break; + case 3: + wepEdit = wep3Edit; + break; + } + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", + network_id, i); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + switch (atoi(reply)) { + case 0: + wep0Radio->setChecked(true); + break; + case 1: + wep1Radio->setChecked(true); + break; + case 2: + wep2Radio->setChecked(true); + break; + case 3: + wep3Radio->setChecked(true); + break; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + idstrEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d priority", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + prioritySpinBox->setValue(atoi(reply)); + } + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + 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 = res.split(QChar(' ')); + eapSelect->insertItems(-1, types); +} + + +void NetworkConfig::useWps() +{ + if (wpagui == NULL) + return; + wpagui->setBssFromScan(bssid); + wpagui->wpsDialog(); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h new file mode 100644 index 0000000000000..0ceeb417f0287 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h @@ -0,0 +1,61 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef NETWORKCONFIG_H +#define NETWORKCONFIG_H + +#include <QObject> +#include "ui_networkconfig.h" + +class WpaGui; + +class NetworkConfig : public QDialog, public Ui::NetworkConfig +{ + Q_OBJECT + +public: + NetworkConfig(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~NetworkConfig(); + + virtual void paramsFromScanResults(QTreeWidgetItem *sel); + virtual void setWpaGui(WpaGui *_wpagui); + virtual int setNetworkParam(int id, const char *field, + const char *value, bool quote); + virtual void paramsFromConfig(int network_id); + virtual void newNetwork(); + +public slots: + virtual void authChanged(int sel); + virtual void addNetwork(); + virtual void encrChanged(const QString &sel); + virtual void writeWepKey(int network_id, QLineEdit *edit, int id); + virtual void removeNetwork(); + virtual void eapChanged(int sel); + virtual void useWps(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int edit_network_id; + bool new_network; + QString bssid; + + virtual void wepEnabled(bool enabled); + virtual void getEapCapa(); +}; + +#endif /* NETWORKCONFIG_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui new file mode 100644 index 0000000000000..ede462f67d910 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui @@ -0,0 +1,425 @@ +<ui version="4.0" > + <class>NetworkConfig</class> + <widget class="QDialog" name="NetworkConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>534</height> + </rect> + </property> + <property name="windowTitle" > + <string>NetworkConfig</string> + </property> + <layout class="QGridLayout" > + <item row="1" column="3" > + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="4" > + <widget class="QFrame" name="frame9" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="ssidEdit" > + <property name="toolTip" > + <string>Network name (Service Set IDentifier)</string> + </property> + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="authLabel" > + <property name="text" > + <string>Authentication</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="authSelect" > + <item> + <property name="text" > + <string>Plaintext 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> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="encrLabel" > + <property name="text" > + <string>Encryption</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="encrSelect" > + <item> + <property name="text" > + <string>None</string> + </property> + </item> + <item> + <property name="text" > + <string>WEP</string> + </property> + </item> + <item> + <property name="text" > + <string>TKIP</string> + </property> + </item> + <item> + <property name="text" > + <string>CCMP</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="pskLabel" > + <property name="text" > + <string>PSK</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="pskEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" > + <string/> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="eapLabel" > + <property name="text" > + <string>EAP method</string> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QComboBox" name="eapSelect" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="identityLabel" > + <property name="text" > + <string>Identity</string> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLineEdit" name="identityEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Username/Identity for EAP methods</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="passwordLabel" > + <property name="text" > + <string>Password</string> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLineEdit" name="passwordEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Password for EAP methods</string> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="cacertLabel" > + <property name="text" > + <string>CA certificate</string> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLineEdit" name="cacertEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="8" column="0" colspan="2" > + <widget class="QGroupBox" name="wepBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>WEP keys</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QRadioButton" name="wep0Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 0</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QRadioButton" name="wep1Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 1</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QRadioButton" name="wep3Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 3</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QRadioButton" name="wep2Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 2</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="wep0Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="wep1Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="wep2Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="wep3Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="9" column="0" colspan="2" > + <widget class="QGroupBox" name="optionalSettingsBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>Optional Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="1" > + <widget class="QLineEdit" name="idstrEdit" > + <property name="toolTip" > + <string>Network Identification String</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QSpinBox" name="prioritySpinBox" > + <property name="toolTip" > + <string>Network Priority</string> + </property> + <property name="maximum" > + <number>10000</number> + </property> + <property name="singleStep" > + <number>10</number> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="idstrLabel" > + <property name="text" > + <string>IDString</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="priorityLabel" > + <property name="text" > + <string>Priority</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="phase2Label" > + <property name="text" > + <string>Inner auth</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="phase2Select" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="useWpsButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>WPS</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>authSelect</tabstop> + <tabstop>encrSelect</tabstop> + <tabstop>pskEdit</tabstop> + <tabstop>eapSelect</tabstop> + <tabstop>identityEdit</tabstop> + <tabstop>passwordEdit</tabstop> + <tabstop>cacertEdit</tabstop> + <tabstop>wep0Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Radio</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>idstrEdit</tabstop> + <tabstop>prioritySpinBox</tabstop> + <tabstop>phase2Select</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> + </tabstops> + <includes> + <include location="global" >qtreewidget.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp new file mode 100644 index 0000000000000..459aa8c69637c --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp @@ -0,0 +1,144 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> + +#include "scanresults.h" +#include "wpagui.h" +#include "networkconfig.h" + + +ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scanRequest())); + connect(scanResultsWidget, + SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, + SLOT(bssSelected(QTreeWidgetItem *))); + + wpagui = NULL; + scanResultsWidget->setItemsExpandable(FALSE); + scanResultsWidget->setRootIsDecorated(FALSE); +} + + +ScanResults::~ScanResults() +{ +} + + +void ScanResults::languageChange() +{ + retranslateUi(this); +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); +} + + +void ScanResults::updateResults() +{ + char reply[2048]; + size_t reply_len; + int index; + char cmd[20]; + + scanResultsWidget->clear(); + + index = 0; + while (wpagui) { + snprintf(cmd, sizeof(cmd), "BSS %d", index++); + if (index > 1000) + break; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + break; + reply[reply_len] = '\0'; + + QString bss(reply); + if (bss.isEmpty() || bss.startsWith("FAIL")) + break; + + QString ssid, bssid, freq, signal, flags; + + QStringList lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + bssid = (*it).mid(pos); + else if ((*it).startsWith("freq=")) + freq = (*it).mid(pos); + else if ((*it).startsWith("qual=")) + signal = (*it).mid(pos); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + } + + QTreeWidgetItem *item = new QTreeWidgetItem(scanResultsWidget); + if (item) { + item->setText(0, ssid); + item->setText(1, bssid); + item->setText(2, freq); + item->setText(3, signal); + item->setText(4, flags); + } + + if (bssid.isEmpty()) + break; + } +} + + +void ScanResults::scanRequest() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) + return; + + wpagui->ctrlRequest("SCAN", reply, &reply_len); +} + + +void ScanResults::getResults() +{ + updateResults(); +} + + +void ScanResults::bssSelected(QTreeWidgetItem *sel) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h new file mode 100644 index 0000000000000..2c4a1b0ace112 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SCANRESULTS_H +#define SCANRESULTS_H + +#include <QObject> +#include "ui_scanresults.h" + +class WpaGui; + +class ScanResults : public QDialog, public Ui::ScanResults +{ + Q_OBJECT + +public: + ScanResults(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~ScanResults(); + +public slots: + virtual void setWpaGui(WpaGui *_wpagui); + virtual void updateResults(); + virtual void scanRequest(); + virtual void getResults(); + virtual void bssSelected(QTreeWidgetItem *sel); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; +}; + +#endif /* SCANRESULTS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/wpa_supplicant/wpa_gui-qt4/scanresults.ui new file mode 100644 index 0000000000000..81e405efc3198 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.ui @@ -0,0 +1,94 @@ +<ui version="4.0" > + <class>ScanResults</class> + <widget class="QDialog" name="ScanResults" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>244</height> + </rect> + </property> + <property name="windowTitle" > + <string>Scan results</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QTreeWidget" name="scanResultsWidget" > + <property name="editTriggers" > + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <property name="sortingEnabled" > + <bool>true</bool> + </property> + <property name="columnCount" > + <number>5</number> + </property> + <column> + <property name="text" > + <string>SSID</string> + </property> + </column> + <column> + <property name="text" > + <string>BSSID</string> + </property> + </column> + <column> + <property name="text" > + <string>frequency</string> + </property> + </column> + <column> + <property name="text" > + <string>signal</string> + </property> + </column> + <column> + <property name="text" > + <string>flags</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp new file mode 100644 index 0000000000000..42fbdbc7b4e42 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp @@ -0,0 +1,100 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "userdatarequest.h" +#include "wpagui.h" +#include "wpa_ctrl.h" + + +UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool, + Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(buttonOk, SIGNAL(clicked()), this, SLOT(sendReply())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(queryEdit, SIGNAL(returnPressed()), this, SLOT(sendReply())); +} + + +UserDataRequest::~UserDataRequest() +{ +} + + +void UserDataRequest::languageChange() +{ + retranslateUi(this); +} + + +int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) +{ + char *tmp, *pos, *pos2; + wpagui = _wpagui; + tmp = strdup(reqMsg); + if (tmp == NULL) + return -1; + pos = strchr(tmp, '-'); + if (pos == NULL) { + free(tmp); + return -1; + } + *pos++ = '\0'; + field = tmp; + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + free(tmp); + return -1; + } + *pos2++ = '\0'; + + networkid = atoi(pos); + queryInfo->setText(pos2); + if (strcmp(tmp, "PASSWORD") == 0) { + queryField->setText("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.toAscii().constData(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h new file mode 100644 index 0000000000000..2b6e8371bc65b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef USERDATAREQUEST_H +#define USERDATAREQUEST_H + +#include <QObject> +#include "ui_userdatarequest.h" + +class WpaGui; + +class UserDataRequest : public QDialog, public Ui::UserDataRequest +{ + Q_OBJECT + +public: + UserDataRequest(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~UserDataRequest(); + + int setParams(WpaGui *_wpagui, const char *reqMsg); + +public slots: + virtual void sendReply(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int networkid; + QString field; +}; + +#endif /* USERDATAREQUEST_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui new file mode 100644 index 0000000000000..1de2a26da1cd0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui @@ -0,0 +1,109 @@ +<ui version="4.0" stdsetdef="1" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>UserDataRequest</class> + <widget class="QDialog" name="UserDataRequest" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="windowTitle" > + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="queryInfo" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="queryField" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="queryEdit" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <spacer name="spacer4" > + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="sizeType" > + <enum>Expanding</enum> + </property> + <property name="orientation" > + <enum>Horizontal</enum> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonOk" > + <property name="text" > + <string>&OK</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="text" > + <string>&Cancel</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop new file mode 100644 index 0000000000000..ccc7d8741d02b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Name=wpa_gui +Comment=Graphical user interface for wpa_supplicant +Exec=wpa_gui +Icon=wpa_gui +GenericName=wpa_supplicant user interface +Terminal=false +Type=Application +Categories=Qt;Network; diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro new file mode 100644 index 0000000000000..2317cbd48d043 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro @@ -0,0 +1,62 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt warn_on release + +DEFINES += CONFIG_CTRL_IFACE + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else:win32-x-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + DEFINES += _X86_ + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src/utils ../../src/common + +HEADERS += wpamsg.h \ + wpagui.h \ + eventhistory.h \ + scanresults.h \ + userdatarequest.h \ + networkconfig.h \ + addinterface.h + +SOURCES += main.cpp \ + wpagui.cpp \ + eventhistory.cpp \ + scanresults.cpp \ + userdatarequest.cpp \ + networkconfig.cpp \ + addinterface.cpp \ + ../../src/common/wpa_ctrl.c + +RESOURCES += icons.qrc + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp new file mode 100644 index 0000000000000..dcd33b9450f2e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -0,0 +1,1699 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include <cstdio> +#include <QMessageBox> +#include <QCloseEvent> +#include <QImageReader> +#include <QSettings> + +#include "wpagui.h" +#include "dirent.h" +#include "wpa_ctrl.h" +#include "userdatarequest.h" +#include "networkconfig.h" + +#if 1 +/* Silence stdout */ +#define printf wpagui_printf +static int wpagui_printf(const char *, ...) +{ + return 0; +} +#endif + +WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags) + : QMainWindow(parent), app(_app) +{ + setupUi(this); + +#ifdef CONFIG_NATIVE_WINDOWS + fileStopServiceAction = new QAction(this); + fileStopServiceAction->setObjectName("Stop Service"); + fileStopServiceAction->setIconText("Stop Service"); + fileMenu->insertAction(actionWPS, fileStopServiceAction); + + fileStartServiceAction = new QAction(this); + fileStartServiceAction->setObjectName("Start Service"); + fileStartServiceAction->setIconText("Start Service"); + fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction); + + connect(fileStartServiceAction, SIGNAL(triggered()), this, + SLOT(startService())); + connect(fileStopServiceAction, SIGNAL(triggered()), this, + SLOT(stopService())); + + addInterfaceAction = new QAction(this); + addInterfaceAction->setIconText("Add Interface"); + fileMenu->insertAction(fileStartServiceAction, addInterfaceAction); + + connect(addInterfaceAction, SIGNAL(triggered()), this, + SLOT(addInterface())); +#endif /* CONFIG_NATIVE_WINDOWS */ + + (void) statusBar(); + + /* + * Disable WPS tab by default; it will be enabled if wpa_supplicant is + * built with WPS support. + */ + wpsTab->setEnabled(false); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false); + + connect(fileEventHistoryAction, SIGNAL(triggered()), this, + SLOT(eventHistory())); + connect(fileSaveConfigAction, SIGNAL(triggered()), this, + SLOT(saveConfig())); + connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog())); + connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(networkAddAction, SIGNAL(triggered()), this, + SLOT(addNetwork())); + connect(networkEditAction, SIGNAL(triggered()), this, + SLOT(editSelectedNetwork())); + connect(networkRemoveAction, SIGNAL(triggered()), this, + SLOT(removeSelectedNetwork())); + connect(networkEnableAllAction, SIGNAL(triggered()), this, + SLOT(enableAllNetworks())); + connect(networkDisableAllAction, SIGNAL(triggered()), this, + SLOT(disableAllNetworks())); + connect(networkRemoveAllAction, SIGNAL(triggered()), this, + SLOT(removeAllNetworks())); + connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex())); + connect(helpContentsAction, SIGNAL(triggered()), this, + SLOT(helpContents())); + connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout())); + connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB())); + connect(adapterSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectAdapter(const QString&))); + connect(networkSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectNetwork(const QString&))); + connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(editNetworkButton, SIGNAL(clicked()), this, + SLOT(editListedNetwork())); + connect(removeNetworkButton, SIGNAL(clicked()), this, + SLOT(removeListedNetwork())); + connect(networkList, SIGNAL(itemSelectionChanged()), this, + SLOT(updateNetworkDisabledStatus())); + connect(enableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(enableListedNetwork(bool))); + connect(disableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(disableListedNetwork(bool))); + connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + this, SLOT(editListedNetwork())); + connect(wpaguiTab, SIGNAL(currentChanged(int)), this, + SLOT(tabChanged(int))); + connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc())); + connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin())); + connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this, + SLOT(wpsApPinChanged(const QString &))); + connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin())); + + eh = NULL; + scanres = NULL; + add_iface = NULL; + udr = NULL; + tray_icon = NULL; + startInTray = false; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + parse_argv(); + + if (app->isSessionRestored()) { + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + if (app->sessionId().compare(settings.value("session_id"). + toString()) == 0) + startInTray = settings.value("in_tray").toBool(); + settings.endGroup(); + } + + if (QSystemTrayIcon::isSystemTrayAvailable()) + createTrayIcon(startInTray); + else + show(); + + connectedToService = false; + textStatus->setText("connecting to wpa_supplicant"); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->setSingleShot(FALSE); + timer->start(1000); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +WpaGui::~WpaGui() +{ + delete msgNotifier; + + if (monitor_conn) { + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (add_iface) { + add_iface->close(); + delete add_iface; + add_iface = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::languageChange() +{ + retranslateUi(this); +} + + +void WpaGui::parse_argv() +{ + int c; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:t"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + case 't': + startInTray = true; + break; + } + } +} + + +int WpaGui::openCtrlConnection(const char *ifname) +{ + char *cfile; + int flen; + char buf[2048], *pos, *pos2; + size_t len; + + if (ifname) { + if (ifname != ctrl_iface) { + free(ctrl_iface); + ctrl_iface = strdup(ifname); + } + } else { +#ifdef CONFIG_CTRL_IFACE_UDP + free(ctrl_iface); + ctrl_iface = strdup("udp"); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + ctrl_iface = strdup(dent->d_name); + break; + } + closedir(dir); + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + + free(ctrl_iface); + ctrl_iface = NULL; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl) { + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, + &len, NULL); + if (ret >= 0) { + connectedToService = true; + buf[len] = '\0'; + pos = strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ctrl_iface = strdup(buf); + } + wpa_ctrl_close(ctrl); + } +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + } + + if (ctrl_iface == NULL) { +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && !serviceRunning()) { + first = false; + if (QMessageBox::warning( + this, qAppName(), + "wpa_supplicant service is not running.\n" + "Do you want to start it?", + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + startService(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return -1; + } + +#ifdef CONFIG_CTRL_IFACE_UNIX + flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); +#else /* CONFIG_CTRL_IFACE_UNIX */ + flen = strlen(ctrl_iface) + 1; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s", ctrl_iface); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn) { + delete msgNotifier; + msgNotifier = NULL; + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + printf("Trying to connect to '%s'\n", cfile); + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + free(cfile); + return -1; + } + monitor_conn = wpa_ctrl_open(cfile); + free(cfile); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + return -1; + } + if (wpa_ctrl_attach(monitor_conn)) { + printf("Failed to attach to wpa_supplicant\n"); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); +#endif + + adapterSelect->clear(); + adapterSelect->addItem(ctrl_iface); + adapterSelect->setCurrentIndex(0); + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= + 0) { + buf[len] = '\0'; + pos = buf; + while (*pos) { + pos2 = strchr(pos, '\n'); + if (pos2) + *pos2 = '\0'; + if (strcmp(pos, ctrl_iface) != 0) + adapterSelect->addItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len, + NULL) >= 0) { + buf[len] = '\0'; + + QString res(buf); + QStringList types = res.split(QChar(' ')); + bool wps = types.contains("WSC"); + actionWPS->setEnabled(wps); + wpsTab->setEnabled(wps); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps); + } + + return 0; +} + + +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(); + +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && connectedToService && + (ctrl_iface == NULL || *ctrl_iface == '\0')) { + first = false; + if (QMessageBox::information( + this, qAppName(), + "No network interfaces in use.\n" + "Would you like to add one?", + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + addInterface(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return; + } + + buf[len] = '\0'; + + bool auth_updated = false, ssid_updated = false; + bool bssid_updated = false, ipaddr_updated = false; + bool status_updated = false; + char *pairwise_cipher = NULL, *group_cipher = NULL; + + start = buf; + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + pos = strchr(start, '='); + if (pos) { + *pos++ = '\0'; + if (strcmp(start, "bssid") == 0) { + bssid_updated = true; + textBssid->setText(pos); + } else if (strcmp(start, "ssid") == 0) { + ssid_updated = true; + textSsid->setText(pos); + } else if (strcmp(start, "ip_address") == 0) { + ipaddr_updated = true; + textIpAddress->setText(pos); + } else if (strcmp(start, "wpa_state") == 0) { + status_updated = true; + textStatus->setText(pos); + } else if (strcmp(start, "key_mgmt") == 0) { + auth_updated = true; + textAuthentication->setText(pos); + /* TODO: could add EAP status to this */ + } else if (strcmp(start, "pairwise_cipher") == 0) { + pairwise_cipher = pos; + } else if (strcmp(start, "group_cipher") == 0) { + group_cipher = pos; + } + } + + if (last) + break; + start = end + 1; + } + + if (pairwise_cipher || group_cipher) { + QString encr; + if (pairwise_cipher && group_cipher && + strcmp(pairwise_cipher, group_cipher) != 0) { + encr.append(pairwise_cipher); + encr.append(" + "); + encr.append(group_cipher); + } else if (pairwise_cipher) { + encr.append(pairwise_cipher); + } else { + encr.append(group_cipher); + encr.append(" [group key only]"); + } + textEncryption->setText(encr); + } else + textEncryption->clear(); + + if (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + int was_selected = -1; + bool current = false; + + if (!networkMayHaveChanged) + return; + + if (networkList->currentRow() >= 0) + was_selected = networkList->currentRow(); + + networkSelect->clear(); + networkList->clear(); + + if (ctrl_conn == NULL) + return; + + len = sizeof(buf) - 1; + if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->addItem(network); + networkList->addItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentIndex(networkSelect->count() - + 1); + current = true; + } else if (first_active < 0 && + strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (networkSelect->count() > 1) + networkSelect->addItem("Select any network"); + + if (!current && first_active >= 0) + networkSelect->setCurrentIndex(first_active); + + if (was_selected >= 0 && networkList->count() > 0) { + if (was_selected < networkList->count()) + networkList->setCurrentRow(was_selected); + else + networkList->setCurrentRow(networkList->count() - 1); + } + else + networkList->setCurrentRow(networkSelect->currentIndex()); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2008,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms " + "of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\n" + "\n" + "This product includes software developed\n" + "by the OpenSSL Project for use in the\n" + "OpenSSL Toolkit (http://www.openssl.org/)\n"); +} + + +void WpaGui::disconnect() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("DISCONNECT", reply, &reply_len); + stopWpsRun(false); +} + + +void WpaGui::scan() +{ + if (scanres) { + scanres->close(); + delete scanres; + } + + scanres = new ScanResults(); + if (scanres == NULL) + return; + scanres->setWpaGui(this); + scanres->show(); + scanres->exec(); +} + + +void WpaGui::eventHistory() +{ + if (eh) { + eh->close(); + delete eh; + } + + eh = new EventHistory(); + if (eh == NULL) + return; + eh->addEvents(msgs); + eh->show(); + eh->exec(); +} + + +void WpaGui::ping() +{ + char buf[10]; + size_t len; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + /* + * QSocketNotifier cannot be used with Windows named pipes, so use a + * timer to check for received messages for now. This could be + * optimized be doing something specific to named pipes or Windows + * events, but it is not clear what would be the best way of doing that + * in Qt. + */ + receiveMsgs(); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (scanres && !scanres->isVisible()) { + delete scanres; + scanres = NULL; + } + + if (eh && !eh->isVisible()) { + delete eh; + eh = NULL; + } + + if (udr && !udr->isVisible()) { + delete udr; + udr = NULL; + } + + len = sizeof(buf) - 1; + if (ctrlRequest("PING", buf, &len) < 0) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } + +#ifndef CONFIG_CTRL_IFACE_NAMED_PIPE + /* Use less frequent pings and status updates when the main window is + * hidden (running in taskbar). */ + int interval = isHidden() ? 5000 : 1000; + if (timer->interval() != interval) + timer->setInterval(interval); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +void WpaGui::processMsg(char *msg) +{ + char *pos = msg, *pos2; + int priority = 2; + + if (*pos == '<') { + /* skip priority */ + pos++; + priority = atoi(pos); + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + WpaMsg wm(pos, priority); + if (eh) + eh->addEvent(wm); + msgs.append(wm); + while (msgs.count() > 100) + msgs.pop_front(); + + /* Update last message with truncated version of the event */ + if (strncmp(pos, "CTRL-", 5) == 0) { + pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); + if (pos2) + pos2++; + else + pos2 = pos; + } else + pos2 = pos; + QString lastmsg = pos2; + lastmsg.truncate(40); + textLastMessage->setText(lastmsg); + + pingsToStatusUpdate = 0; + networkMayHaveChanged = true; + + if (str_match(pos, WPA_CTRL_REQ)) + processCtrlReq(pos + strlen(WPA_CTRL_REQ)); + else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres) + scanres->updateResults(); + else if (str_match(pos, WPA_EVENT_DISCONNECTED)) + showTrayMessage(QSystemTrayIcon::Information, 3, + "Disconnected from network."); + else if (str_match(pos, WPA_EVENT_CONNECTED)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Connection to network established."); + QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus())); + stopWpsRun(true); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "in active PBC mode found."); + wpsStatusText->setText("WPS AP in active PBC mode found"); + wpaguiTab->setCurrentWidget(wpsTab); + wpsInstructions->setText("Press the PBC button on the screen " + "to start registration"); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + " in active PIN mode found."); + wpsStatusText->setText("WPS AP with recently selected " + "registrar"); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS)\n" + "AP detected."); + wpsStatusText->setText("WPS AP detected"); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_OVERLAP)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS)\n" + "PBC mode overlap detected."); + wpsStatusText->setText("PBC mode overlap detected"); + wpsInstructions->setText("More than one AP is currently in " + "active WPS PBC mode. Wait couple of " + "minutes and try again"); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) { + wpsStatusText->setText("Network configuration received"); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) { + if (strstr(pos, "(WSC)")) + wpsStatusText->setText("Registration started"); + } else if (str_match(pos, WPS_EVENT_M2D)) { + wpsStatusText->setText("Registrar does not yet know PIN"); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + wpsStatusText->setText("Registration failed"); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpsStatusText->setText("Registration succeeded"); + } +} + + +void WpaGui::processCtrlReq(const char *req) +{ + if (udr) { + udr->close(); + delete udr; + } + udr = new UserDataRequest(); + if (udr == NULL) + return; + if (udr->setParams(this, req) < 0) { + delete udr; + udr = NULL; + return; + } + udr->show(); + udr->exec(); +} + + +void WpaGui::receiveMsgs() +{ + char buf[256]; + size_t len; + + while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { + len = sizeof(buf) - 1; + if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { + buf[len] = '\0'; + processMsg(buf); + } + } +} + + +void WpaGui::connectB() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("REASSOCIATE", reply, &reply_len); +} + + +void WpaGui::selectNetwork( const QString &sel ) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.startsWith("Select any")) { + cmd = "any"; + } else { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); + stopWpsRun(false); +} + + +void WpaGui::enableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (!cmd.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid enableNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("ENABLE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::disableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (!cmd.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid disableNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("DISABLE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::editNetwork(const QString &sel) +{ + QString cmd(sel); + int id = -1; + + if (!cmd.startsWith("Select any")) { + int pos = sel.indexOf(':'); + if (pos < 0) { + printf("Invalid editNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + id = cmd.toInt(); + } + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + if (id >= 0) + nc->paramsFromConfig(id); + else + nc->newNetwork(); + + nc->show(); + nc->exec(); +} + + +void WpaGui::editSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information(this, "No Networks", + "There are no networks to edit.\n"); + return; + } + QString sel(networkSelect->currentText()); + editNetwork(sel); +} + + +void WpaGui::editListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, "Select A Network", + "Select a network from the list to" + " edit it.\n"); + return; + } + QString sel(networkList->currentItem()->text()); + editNetwork(sel); +} + + +void WpaGui::triggerUpdate() +{ + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::addNetwork() +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + nc->newNetwork(); + nc->show(); + nc->exec(); +} + + +void WpaGui::removeNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.startsWith("Select any")) + return; + + if (!cmd.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid removeNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("REMOVE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::removeSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information(this, "No Networks", + "There are no networks to remove.\n"); + return; + } + QString sel(networkSelect->currentText()); + removeNetwork(sel); +} + + +void WpaGui::removeListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, "Select A Network", + "Select a network from the list to" + " remove it.\n"); + return; + } + QString sel(networkList->currentItem()->text()); + removeNetwork(sel); +} + + +void WpaGui::enableAllNetworks() +{ + QString sel("all"); + enableNetwork(sel); +} + + +void WpaGui::disableAllNetworks() +{ + QString sel("all"); + disableNetwork(sel); +} + + +void WpaGui::removeAllNetworks() +{ + QString sel("all"); + removeNetwork(sel); +} + + +int WpaGui::getNetworkDisabled(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply) - 1; + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid getNetworkDisabled '%s'\n", + cmd.toAscii().constData()); + return -1; + } + cmd.truncate(pos); + cmd.prepend("GET_NETWORK "); + cmd.append(" disabled"); + + if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0 + && reply_len >= 1) { + reply[reply_len] = '\0'; + if (!str_match(reply, "FAIL")) + return atoi(reply); + } + + return -1; +} + + +void WpaGui::updateNetworkDisabledStatus() +{ + if (networkList->currentRow() < 0) + return; + + QString sel(networkList->currentItem()->text()); + + switch (getNetworkDisabled(sel)) { + case 0: + if (!enableRadioButton->isChecked()) + enableRadioButton->setChecked(true); + return; + case 1: + if (!disableRadioButton->isChecked()) + disableRadioButton->setChecked(true); + return; + } +} + + +void WpaGui::enableListedNetwork(bool enabled) +{ + if (networkList->currentRow() < 0 || !enabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 1) + enableNetwork(sel); +} + + +void WpaGui::disableListedNetwork(bool disabled) +{ + if (networkList->currentRow() < 0 || !disabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 0) + disableNetwork(sel); +} + + +void WpaGui::saveConfig() +{ + char buf[10]; + size_t len; + + len = sizeof(buf) - 1; + ctrlRequest("SAVE_CONFIG", buf, &len); + + buf[len] = '\0'; + + if (str_match(buf, "FAIL")) + QMessageBox::warning(this, "Failed to save configuration", + "The configuration could not be saved.\n" + "\n" + "The update_config=1 configuration option\n" + "must be used for configuration saving to\n" + "be permitted.\n"); + else + QMessageBox::information(this, "Saved configuration", + "The current configuration was saved." + "\n"); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.toAscii().constData()) < 0) + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} + + +void WpaGui::createTrayIcon(bool trayOnly) +{ + QApplication::setQuitOnLastWindowClosed(false); + + tray_icon = new QSystemTrayIcon(this); + tray_icon->setToolTip(qAppName() + " - wpa_supplicant user interface"); + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg")); + else + tray_icon->setIcon(QIcon(":/icons/wpa_gui.png")); + + connect(tray_icon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + + ackTrayIcon = false; + + tray_menu = new QMenu(this); + + disconnectAction = new QAction("&Disconnect", this); + reconnectAction = new QAction("Re&connect", this); + connect(disconnectAction, SIGNAL(triggered()), this, + SLOT(disconnect())); + connect(reconnectAction, SIGNAL(triggered()), this, + SLOT(connectB())); + tray_menu->addAction(disconnectAction); + tray_menu->addAction(reconnectAction); + tray_menu->addSeparator(); + + eventAction = new QAction("&Event History", this); + scanAction = new QAction("Scan &Results", this); + statAction = new QAction("S&tatus", this); + connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory())); + connect(scanAction, SIGNAL(triggered()), this, SLOT(scan())); + connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus())); + tray_menu->addAction(eventAction); + tray_menu->addAction(scanAction); + tray_menu->addAction(statAction); + tray_menu->addSeparator(); + + showAction = new QAction("&Show Window", this); + hideAction = new QAction("&Hide Window", this); + quitAction = new QAction("&Quit", this); + connect(showAction, SIGNAL(triggered()), this, SLOT(show())); + connect(hideAction, SIGNAL(triggered()), this, SLOT(hide())); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + tray_menu->addAction(showAction); + tray_menu->addAction(hideAction); + tray_menu->addSeparator(); + tray_menu->addAction(quitAction); + + tray_icon->setContextMenu(tray_menu); + + tray_icon->show(); + + if (!trayOnly) + show(); + inTray = trayOnly; +} + + +void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec, + const QString & msg) +{ + if (!QSystemTrayIcon::supportsMessages()) + return; + + if (isVisible() || !tray_icon || !tray_icon->isVisible()) + return; + + tray_icon->showMessage(qAppName(), msg, type, sec * 1000); +} + + +void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how) + { + switch (how) { + /* use close() here instead of hide() and allow the + * custom closeEvent handler take care of children */ + case QSystemTrayIcon::Trigger: + ackTrayIcon = true; + if (isVisible()) { + close(); + inTray = true; + } else { + show(); + inTray = false; + } + break; + case QSystemTrayIcon::MiddleClick: + showTrayStatus(); + break; + default: + break; + } +} + + +void WpaGui::showTrayStatus() +{ + char buf[2048]; + size_t len; + + len = sizeof(buf) - 1; + if (ctrlRequest("STATUS", buf, &len) < 0) + return; + buf[len] = '\0'; + + QString msg, status(buf); + + QStringList lines = status.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + msg.append("BSSID:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ssid=")) + msg.append("SSID: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("pairwise_cipher=")) + msg.append("PAIR: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("group_cipher=")) + msg.append("GROUP:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("key_mgmt=")) + msg.append("AUTH: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("wpa_state=")) + msg.append("STATE:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ip_address=")) + msg.append("IP: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("Supplicant PAE state=")) + msg.append("PAE: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("EAP state=")) + msg.append("EAP: \t" + (*it).mid(pos) + "\n"); + } + + if (!msg.isEmpty()) + showTrayMessage(QSystemTrayIcon::Information, 10, msg); +} + + +void WpaGui::closeEvent(QCloseEvent *event) +{ + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + if (tray_icon && !ackTrayIcon) { + /* give user a visual hint that the tray icon exists */ + if (QSystemTrayIcon::supportsMessages()) { + hide(); + showTrayMessage(QSystemTrayIcon::Information, 3, + qAppName() + " will keep running in " + "the system tray."); + } else { + QMessageBox::information(this, qAppName() + " systray", + "The program will keep " + "running in the system " + "tray."); + } + ackTrayIcon = true; + } + + event->accept(); +} + + +void WpaGui::wpsDialog() +{ + wpaguiTab->setCurrentWidget(wpsTab); +} + + +void WpaGui::tabChanged(int index) +{ + if (index != 2) + return; + + if (wpsRunning) + return; + + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + if (bssFromScan.isEmpty()) + wpsApPinButton->setEnabled(false); +} + + +void WpaGui::wpsPbc() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0) + return; + + wpsPinEdit->setEnabled(false); + if (wpsStatusText->text().compare("WPS AP in active PBC mode found")) { + wpsInstructions->setText("Press the push button on the AP to " + "start the PBC mode."); + } else { + wpsInstructions->setText("If you have not yet done so, press " + "the push button on the AP to start " + "the PBC mode."); + } + wpsStatusText->setText("Waiting for Registrar"); + wpsRunning = true; +} + + +void WpaGui::wpsGeneratePin() +{ + char reply[20]; + size_t reply_len = sizeof(reply) - 1; + + if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0) + return; + + reply[reply_len] = '\0'; + + wpsPinEdit->setText(reply); + wpsPinEdit->setEnabled(true); + wpsInstructions->setText("Enter the generated PIN into the Registrar " + "(either the internal one in the AP or an " + "external one)."); + wpsStatusText->setText("Waiting for Registrar"); + wpsRunning = true; +} + + +void WpaGui::setBssFromScan(const QString &bssid) +{ + bssFromScan = bssid; + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8); + wpsStatusText->setText("WPS AP selected from scan results"); + wpsInstructions->setText("If you want to use an AP device PIN, e.g., " + "from a label in the device, enter the eight " + "digit AP PIN and click Use AP PIN button."); +} + + +void WpaGui::wpsApPinChanged(const QString &text) +{ + wpsApPinButton->setEnabled(text.length() == 8); +} + + +void WpaGui::wpsApPin() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text()); + if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0) + return; + + wpsStatusText->setText("Waiting for AP/Enrollee"); + wpsRunning = true; +} + + +void WpaGui::stopWpsRun(bool success) +{ + if (wpsRunning) + wpsStatusText->setText(success ? "Connected to the network" : + "Stopped"); + else + wpsStatusText->setText(""); + wpsPinEdit->setEnabled(false); + wpsInstructions->setText(""); + wpsRunning = false; + bssFromScan = ""; + wpsApPinEdit->setEnabled(false); + wpsApPinButton->setEnabled(false); +} + + +#ifdef CONFIG_NATIVE_WINDOWS + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif + +class ErrorMsg : public QMessageBox { +public: + ErrorMsg(QWidget *parent, DWORD last_err = GetLastError()); + void showMsg(QString msg); +private: + DWORD err; +}; + +ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) : + QMessageBox(parent), err(last_err) +{ + setWindowTitle("wpa_gui error"); + setIcon(QMessageBox::Warning); +} + +void ErrorMsg::showMsg(QString msg) +{ + LPTSTR buf; + + setText(msg); + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) (void *) &buf, + 0, NULL) > 0) { + QString msg = QString::fromWCharArray(buf); + setInformativeText(QString("[%1] %2").arg(err).arg(msg)); + LocalFree(buf); + } else { + setInformativeText(QString("[%1]").arg(err)); + } + + exec(); +} + + +void WpaGui::startService() +{ + SC_HANDLE svc, scm; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg("OpenSCManager failed"); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_START); + if (!svc) { + ErrorMsg(this).showMsg("OpenService failed"); + CloseServiceHandle(scm); + return; + } + + if (!StartService(svc, 0, NULL)) { + ErrorMsg(this).showMsg("Failed to start wpa_supplicant " + "service"); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +void WpaGui::stopService() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg("OpenSCManager failed"); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP); + if (!svc) { + ErrorMsg(this).showMsg("OpenService failed"); + CloseServiceHandle(scm); + return; + } + + if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) { + ErrorMsg(this).showMsg("Failed to stop wpa_supplicant " + "service"); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +bool WpaGui::serviceRunning() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + bool running = false; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return false; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS); + if (!svc) { + printf("OpenService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return false; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) + running = true; + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return running; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void WpaGui::addInterface() +{ + if (add_iface) { + add_iface->close(); + delete add_iface; + } + add_iface = new AddInterface(this, this); + add_iface->show(); + add_iface->exec(); +} + + +void WpaGui::saveState() +{ + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + settings.setValue("session_id", app->sessionId()); + settings.setValue("in_tray", inTray); + settings.endGroup(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h new file mode 100644 index 0000000000000..a53396541e645 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h @@ -0,0 +1,145 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPAGUI_H +#define WPAGUI_H + +#include <QSystemTrayIcon> +#include <QObject> +#include "ui_wpagui.h" +#include "addinterface.h" + +class UserDataRequest; + + +class WpaGui : public QMainWindow, public Ui::WpaGui +{ + Q_OBJECT + +public: + WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0, + Qt::WFlags fl = 0); + ~WpaGui(); + + virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen); + virtual void triggerUpdate(); + virtual void editNetwork(const QString &sel); + virtual void removeNetwork(const QString &sel); + virtual void enableNetwork(const QString &sel); + virtual void disableNetwork(const QString &sel); + virtual int getNetworkDisabled(const QString &sel); + void setBssFromScan(const QString &bssid); + void saveState(); + +public slots: + virtual void parse_argv(); + virtual void updateStatus(); + virtual void updateNetworks(); + virtual void helpIndex(); + virtual void helpContents(); + virtual void helpAbout(); + virtual void disconnect(); + virtual void scan(); + virtual void eventHistory(); + virtual void ping(); + virtual void processMsg(char *msg); + virtual void processCtrlReq(const char *req); + virtual void receiveMsgs(); + virtual void connectB(); + virtual void selectNetwork(const QString &sel); + virtual void editSelectedNetwork(); + virtual void editListedNetwork(); + virtual void removeSelectedNetwork(); + virtual void removeListedNetwork(); + virtual void addNetwork(); + virtual void enableAllNetworks(); + virtual void disableAllNetworks(); + virtual void removeAllNetworks(); + virtual void saveConfig(); + virtual void selectAdapter(const QString &sel); + virtual void updateNetworkDisabledStatus(); + virtual void enableListedNetwork(bool); + virtual void disableListedNetwork(bool); + virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type, + int sec, const QString &msg); + virtual void showTrayStatus(); + virtual void wpsDialog(); + virtual void tabChanged(int index); + virtual void wpsPbc(); + virtual void wpsGeneratePin(); + virtual void wpsApPinChanged(const QString &text); + virtual void wpsApPin(); +#ifdef CONFIG_NATIVE_WINDOWS + virtual void startService(); + virtual void stopService(); +#endif /* CONFIG_NATIVE_WINDOWS */ + virtual void addInterface(); + +protected slots: + virtual void languageChange(); + virtual void trayActivated(QSystemTrayIcon::ActivationReason how); + virtual void closeEvent(QCloseEvent *event); + +private: + ScanResults *scanres; + bool networkMayHaveChanged; + char *ctrl_iface; + EventHistory *eh; + struct wpa_ctrl *ctrl_conn; + QSocketNotifier *msgNotifier; + QTimer *timer; + int pingsToStatusUpdate; + WpaMsgList msgs; + char *ctrl_iface_dir; + struct wpa_ctrl *monitor_conn; + UserDataRequest *udr; + QAction *disconnectAction; + QAction *reconnectAction; + QAction *eventAction; + QAction *scanAction; + QAction *statAction; + QAction *showAction; + QAction *hideAction; + QAction *quitAction; + QMenu *tray_menu; + QSystemTrayIcon *tray_icon; + void createTrayIcon(bool); + bool ackTrayIcon; + bool startInTray; + + int openCtrlConnection(const char *ifname); + + bool wpsRunning; + + QString bssFromScan; + + void stopWpsRun(bool success); + +#ifdef CONFIG_NATIVE_WINDOWS + QAction *fileStartServiceAction; + QAction *fileStopServiceAction; + + bool serviceRunning(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + QAction *addInterfaceAction; + AddInterface *add_iface; + + bool connectedToService; + + QApplication *app; + bool inTray; +}; + +#endif /* WPAGUI_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/wpa_supplicant/wpa_gui-qt4/wpagui.ui new file mode 100644 index 0000000000000..cd67ecb783011 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.ui @@ -0,0 +1,517 @@ +<ui version="4.0" > + <class>WpaGui</class> + <widget class="QMainWindow" name="WpaGui" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>330</height> + </rect> + </property> + <property name="windowTitle" > + <string>wpa_gui</string> + </property> + <property name="windowIcon" > + <iconset resource="icons.qrc" > + <normaloff>:/icons/wpa_gui.svg</normaloff>:/icons/wpa_gui.svg</iconset> + </property> + <widget class="QWidget" name="widget" > + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="adapterLabel" > + <property name="text" > + <string>Adapter:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="adapterSelect" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="networkLabel" > + <property name="text" > + <string>Network:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="networkSelect" /> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QTabWidget" name="wpaguiTab" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="statusTab" > + <attribute name="title" > + <string>Current Status</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QFrame" name="frame3" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="statusLabel" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="lastMessageLabel" > + <property name="text" > + <string>Last message:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="authenticationLabel" > + <property name="text" > + <string>Authentication:</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="encryptionLabel" > + <property name="text" > + <string>Encryption:</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID:</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="bssidLabel" > + <property name="text" > + <string>BSSID:</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="ipAddressLabel" > + <property name="text" > + <string>IP address:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="textStatus" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLabel" name="textLastMessage" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="textAuthentication" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="textEncryption" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLabel" name="textSsid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="textBssid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLabel" name="textIpAddress" > + <property name="text" > + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="connectButton" > + <property name="text" > + <string>Connect</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="disconnectButton" > + <property name="text" > + <string>Disconnect</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="networkconfigTab" > + <attribute name="title" > + <string>Manage Networks</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QListWidget" name="networkList" > + <property name="selectionRectVisible" > + <bool>true</bool> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QRadioButton" name="enableRadioButton" > + <property name="text" > + <string>Enabled</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="editNetworkButton" > + <property name="text" > + <string>Edit</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeNetworkButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <widget class="QRadioButton" name="disableRadioButton" > + <property name="text" > + <string>Disabled</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QPushButton" name="addNetworkButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="scanNetworkButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="wpsTab" > + <attribute name="title" > + <string>WPS</string> + </attribute> + <layout class="QGridLayout" name="wpsGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLabel" name="wpsStatusText" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPbcButton" > + <property name="text" > + <string>PBC - push button</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPinButton" > + <property name="text" > + <string>Generate PIN</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>PIN:</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QLineEdit" name="wpsPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2" > + <widget class="QPushButton" name="wpsApPinButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Use AP PIN</string> + </property> + </widget> + </item> + <item row="3" column="2" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>AP PIN:</string> + </property> + </widget> + </item> + <item row="3" column="3" > + <widget class="QLineEdit" name="wpsApPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="0" colspan="4" > + <widget class="QTextEdit" name="wpsInstructions" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>24</height> + </rect> + </property> + <widget class="QMenu" name="fileMenu" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileEventHistoryAction" /> + <addaction name="fileSaveConfigAction" /> + <addaction name="actionWPS" /> + <addaction name="separator" /> + <addaction name="fileExitAction" /> + </widget> + <widget class="QMenu" name="networkMenu" > + <property name="title" > + <string>&Network</string> + </property> + <addaction name="networkAddAction" /> + <addaction name="networkEditAction" /> + <addaction name="networkRemoveAction" /> + <addaction name="separator" /> + <addaction name="networkEnableAllAction" /> + <addaction name="networkDisableAllAction" /> + <addaction name="networkRemoveAllAction" /> + </widget> + <widget class="QMenu" name="helpMenu" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpContentsAction" /> + <addaction name="helpIndexAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + </widget> + <addaction name="fileMenu" /> + <addaction name="networkMenu" /> + <addaction name="helpMenu" /> + </widget> + <action name="fileEventHistoryAction" > + <property name="text" > + <string>Event &History</string> + </property> + </action> + <action name="fileSaveConfigAction" > + <property name="text" > + <string>&Save Configuration</string> + </property> + <property name="shortcut" > + <string>Ctrl+S</string> + </property> + </action> + <action name="fileExitAction" > + <property name="text" > + <string>E&xit</string> + </property> + <property name="shortcut" > + <string>Ctrl+Q</string> + </property> + </action> + <action name="networkAddAction" > + <property name="text" > + <string>&Add</string> + </property> + </action> + <action name="networkEditAction" > + <property name="text" > + <string>&Edit</string> + </property> + </action> + <action name="networkRemoveAction" > + <property name="text" > + <string>&Remove</string> + </property> + </action> + <action name="networkEnableAllAction" > + <property name="text" > + <string>E&nable All</string> + </property> + </action> + <action name="networkDisableAllAction" > + <property name="text" > + <string>&Disable All</string> + </property> + </action> + <action name="networkRemoveAllAction" > + <property name="text" > + <string>Re&move All</string> + </property> + </action> + <action name="helpContentsAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Contents...</string> + </property> + </action> + <action name="helpIndexAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Index...</string> + </property> + </action> + <action name="helpAboutAction" > + <property name="text" > + <string>&About</string> + </property> + </action> + <action name="actionWPS" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Wi-Fi Protected Setup</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="global" >qtimer.h</include> + <include location="global" >qsocketnotifier.h</include> + <include location="local" >wpamsg.h</include> + <include location="local" >eventhistory.h</include> + <include location="local" >scanresults.h</include> + </includes> + <resources> + <include location="icons.qrc" /> + </resources> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/wpa_supplicant/wpa_gui-qt4/wpamsg.h new file mode 100644 index 0000000000000..4950b213a2e3d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpamsg.h @@ -0,0 +1,41 @@ +/* + * wpa_gui - WpaMsg class for storing event messages + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPAMSG_H +#define WPAMSG_H + +#include <QDateTime> +#include <QLinkedList> + +class WpaMsg { +public: + WpaMsg(const QString &_msg, int _priority = 2) + : msg(_msg), priority(_priority) + { + timestamp = QDateTime::currentDateTime(); + } + + QString getMsg() const { return msg; } + int getPriority() const { return priority; } + QDateTime getTimestamp() const { return timestamp; } + +private: + QString msg; + int priority; + QDateTime timestamp; +}; + +typedef QLinkedList<WpaMsg> WpaMsgList; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_gui/.gitignore b/wpa_supplicant/wpa_gui/.gitignore new file mode 100644 index 0000000000000..11963c84daa33 --- /dev/null +++ b/wpa_supplicant/wpa_gui/.gitignore @@ -0,0 +1,5 @@ +.moc +.obj +.ui +Makefile +wpa_gui diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui b/wpa_supplicant/wpa_gui/eventhistory.ui new file mode 100644 index 0000000000000..3735fb70e8f94 --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui @@ -0,0 +1,125 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>EventHistory</class> +<widget class="QDialog"> + <property name="name"> + <cstring>EventHistory</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="caption"> + <string>Event history</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Timestamp</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Message</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>eventListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="resizePolicy"> + <enum>Manual</enum> + </property> + <property name="selectionMode"> + <enum>NoSelection</enum> + </property> + <property name="resizeMode"> + <enum>LastColumn</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout30</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>EventHistory</receiver> + <slot>close()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in implementation">eventhistory.ui.h</include> +</includes> +<slots> + <slot>addEvents( WpaMsgList msgs )</slot> + <slot>addEvent( WpaMsg msg )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui.h b/wpa_supplicant/wpa_gui/eventhistory.ui.h new file mode 100644 index 0000000000000..cb2caab2e4e18 --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void EventHistory::init() +{ +} + + +void EventHistory::destroy() +{ +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) { + addEvent(*it); + } +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + Q3ListViewItem *item; + item = new Q3ListViewItem(eventListView, + msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + if (item == NULL) + return; + eventListView->setSelected(item, false); +} diff --git a/wpa_supplicant/wpa_gui/main.cpp b/wpa_supplicant/wpa_gui/main.cpp new file mode 100644 index 0000000000000..a78473a8c6b10 --- /dev/null +++ b/wpa_supplicant/wpa_gui/main.cpp @@ -0,0 +1,30 @@ +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <qapplication.h> +#include "wpagui.h" + +int main( int argc, char ** argv ) +{ + QApplication a( argc, argv ); + WpaGui w; + int ret; + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + w.show(); + a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); + ret = a.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui b/wpa_supplicant/wpa_gui/networkconfig.ui new file mode 100644 index 0000000000000..019ecf7e259d0 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui @@ -0,0 +1,475 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NetworkConfig</class> +<widget class="QDialog"> + <property name="name"> + <cstring>NetworkConfig</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>380</width> + <height>430</height> + </rect> + </property> + <property name="caption"> + <string>NetworkConfig</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="1" column="3"> + <property name="name"> + <cstring>cancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + <widget class="QFrame" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame9</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel0</cstring> + </property> + <property name="text"> + <string>SSID</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>ssidEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network name (Service Set IDentifier)</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Network ID</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>idstrEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network Identification String</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Authentication</string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Plaintext or static WEP</string> + </property> + </item> + <item> + <property name="text"> + <string>IEEE 802.1X</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA-Enterprise (EAP)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA2-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA2-Enterprise (EAP)</string> + </property> + </item> + <property name="name"> + <cstring>authSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Encryption</string> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>WEP</string> + </property> + </item> + <item> + <property name="text"> + <string>TKIP</string> + </property> + </item> + <item> + <property name="text"> + <string>CCMP</string> + </property> + </item> + <property name="name"> + <cstring>encrSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>PSK</string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>pskEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>EAP method</string> + </property> + </widget> + <widget class="QComboBox" row="5" column="1"> + <property name="name"> + <cstring>eapSelect</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Identity</string> + </property> + </widget> + <widget class="QLineEdit" row="6" column="1"> + <property name="name"> + <cstring>identityEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Username/Identity for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="7" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QLineEdit" row="7" column="1"> + <property name="name"> + <cstring>passwordEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Password for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="8" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>CA certificate</string> + </property> + </widget> + <widget class="QLineEdit" row="8" column="1"> + <property name="name"> + <cstring>cacertEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QButtonGroup" row="9" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>WEP keys</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>wep0Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 0</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>wep1Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 1</string> + </property> + </widget> + <widget class="QRadioButton" row="3" column="0"> + <property name="name"> + <cstring>wep3Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 3</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>wep2Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 2</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>wep0Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>wep1Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>wep2Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>wep3Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>130</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="QPushButton" row="1" column="2"> + <property name="name"> + <cstring>removeButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>authSelect</sender> + <signal>activated(int)</signal> + <receiver>NetworkConfig</receiver> + <slot>authChanged(int)</slot> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>addButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>encrSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>NetworkConfig</receiver> + <slot>encrChanged(const QString&)</slot> + </connection> + <connection> + <sender>removeButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>removeNetwork()</slot> + </connection> +</connections> +<tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>idstrEdit</tabstop> + <tabstop>authSelect</tabstop> + <tabstop>encrSelect</tabstop> + <tabstop>pskEdit</tabstop> + <tabstop>eapSelect</tabstop> + <tabstop>identityEdit</tabstop> + <tabstop>passwordEdit</tabstop> + <tabstop>cacertEdit</tabstop> + <tabstop>wep0Radio</tabstop> + <tabstop>wep1Radio</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">qlistview.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int edit_network_id;</variable> + <variable access="private">bool new_network;</variable> +</variables> +<slots> + <slot>authChanged( int sel )</slot> + <slot>addNetwork()</slot> + <slot>encrChanged( const QString & sel )</slot> + <slot>writeWepKey( int network_id, QLineEdit * edit, int id )</slot> + <slot>removeNetwork()</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function>paramsFromScanResults( QListViewItem * sel )</function> + <function>setWpaGui( WpaGui * _wpagui )</function> + <function returnType="int">setNetworkParam( int id, const char * field, const char * value, bool quote )</function> + <function access="private">wepEnabled( bool enabled )</function> + <function>paramsFromConfig( int network_id )</function> + <function>newNetwork()</function> + <function access="private">getEapCapa()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui.h b/wpa_supplicant/wpa_gui/networkconfig.ui.h new file mode 100644 index 0000000000000..501d5d26556a3 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui.h @@ -0,0 +1,552 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + +void NetworkConfig::init() +{ + wpagui = NULL; + new_network = false; +} + +void NetworkConfig::paramsFromScanResults(Q3ListViewItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setCaption(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.find("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.find("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.find("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.find("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE; + + if (flags.find("-CCMP") >= 0) + encr = 1; + else if (flags.find("-TKIP") >= 0) + encr = 0; + else if (flags.find("WEP") >= 0) + encr = 1; + else + encr = 0; + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + + getEapCapa(); +} + + +void NetworkConfig::authChanged(int sel) +{ + pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK); + bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP || + sel == AUTH_WPA2_EAP; + eapSelect->setEnabled(eap); + identityEdit->setEnabled(eap); + passwordEdit->setEnabled(eap); + cacertEdit->setEnabled(eap); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) { + encrSelect->insertItem("None"); + encrSelect->insertItem("WEP"); + encrSelect->setCurrentItem(sel == AUTH_NONE ? 0 : 1); + } else { + encrSelect->insertItem("TKIP"); + encrSelect->insertItem("CCMP"); + encrSelect->setCurrentItem((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_IEEE8021X); +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentItem(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning(this, "wpa_gui", "WPA-PSK requires a passphrase " + "of 8 to 63 characters\n" + "or 64 hex digit PSK"); + return; + } + } + + if (wpagui == NULL) + return; + + memset(reply, 0, sizeof(reply)); + reply_len = sizeof(reply) - 1; + + if (new_network) { + wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); + if (reply[0] == 'F') { + QMessageBox::warning(this, "wpa_gui", "Failed to add network to wpa_supplicant\n" + "configuration."); + return; + } + id = atoi(reply); + } else { + id = edit_network_id; + } + + setNetworkParam(id, "ssid", ssidEdit->text().ascii(), true); + + if (idstrEdit->isEnabled()) + setNetworkParam(id, "id_str", idstrEdit->text().ascii(), true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE: + key_mgmt = "NONE"; + break; + case AUTH_IEEE8021X: + key_mgmt = "IEEE8021X"; + break; + case AUTH_WPA_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA"; + break; + case AUTH_WPA_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA"; + break; + case AUTH_WPA2_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA2"; + break; + case AUTH_WPA2_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA2"; + break; + } + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || + auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { + int encr = encrSelect->currentItem(); + if (encr == 0) + pairwise = "TKIP"; + else + pairwise = "CCMP"; + } + + if (proto) + setNetworkParam(id, "proto", proto, false); + if (key_mgmt) + setNetworkParam(id, "key_mgmt", key_mgmt, false); + if (pairwise) { + setNetworkParam(id, "pairwise", pairwise, false); + setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); + } + if (pskEdit->isEnabled() && + strcmp(pskEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", pskEdit->text().ascii(), psklen != 64); + if (eapSelect->isEnabled()) + setNetworkParam(id, "eap", eapSelect->currentText().ascii(), false); + if (identityEdit->isEnabled()) + setNetworkParam(id, "identity", identityEdit->text().ascii(), true); + if (passwordEdit->isEnabled() && + strcmp(passwordEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", passwordEdit->text().ascii(), true); + if (cacertEdit->isEnabled()) + setNetworkParam(id, "ca_cert", cacertEdit->text().ascii(), true); + writeWepKey(id, wep0Edit, 0); + writeWepKey(id, wep1Edit, 1); + writeWepKey(id, wep2Edit, 2); + writeWepKey(id, wep3Edit, 3); + + if (wep0Radio->isEnabled() && wep0Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "0", false); + else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "1", false); + else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "2", false); + else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "3", false); + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", "Failed to enable network in wpa_supplicant\n" + "configuration."); + /* Network was added, so continue anyway */ + } + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + + close(); +} + + +void NetworkConfig::setWpaGui( WpaGui *_wpagui ) +{ + wpagui = _wpagui; +} + + +int NetworkConfig::setNetworkParam(int id, const char *field, const char *value, bool quote) +{ + char reply[10], cmd[256]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", + id, field, quote ? "\"" : "", value, quote ? "\"" : ""); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + return strncmp(reply, "OK", 2) == 0 ? 0 : -1; +} + + +void NetworkConfig::encrChanged( const QString &sel ) +{ + wepEnabled(sel.find("WEP") == 0); +} + + +void NetworkConfig::wepEnabled( bool enabled ) +{ + wep0Edit->setEnabled(enabled); + wep1Edit->setEnabled(enabled); + wep2Edit->setEnabled(enabled); + wep3Edit->setEnabled(enabled); + wep0Radio->setEnabled(enabled); + wep1Radio->setEnabled(enabled); + wep2Radio->setEnabled(enabled); + wep3Radio->setEnabled(enabled); +} + + +void NetworkConfig::writeWepKey( int network_id, QLineEdit *edit, int id ) +{ + char buf[10]; + bool hex; + const char *txt, *pos; + size_t len; + + if (!edit->isEnabled() || edit->text().isEmpty()) + return; + + /* + * Assume hex key if only hex characters are present and length matches + * with 40, 104, or 128-bit key + */ + txt = edit->text().ascii(); + if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) + return; + len = strlen(txt); + if (len == 0) + return; + pos = txt; + hex = true; + while (*pos) { + if (!((*pos >= '0' && *pos <= '9') || (*pos >= 'a' && *pos <= 'f') || + (*pos >= 'A' && *pos <= 'F'))) { + hex = false; + break; + } + pos++; + } + if (hex && len != 10 && len != 26 && len != 32) + hex = false; + snprintf(buf, sizeof(buf), "wep_key%d", id); + setNetworkParam(network_id, buf, txt, !hex); +} + + +static int key_value_isset(const char *reply, size_t reply_len) +{ + return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); +} + + +void NetworkConfig::paramsFromConfig( int network_id ) +{ + int i, res; + + edit_network_id = network_id; + getEapCapa(); + + char reply[1024], cmd[256], *pos; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + ssidEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + idstrEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); + reply_len = sizeof(reply) - 1; + int wpa = 0; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "RSN") || strstr(reply, "WPA2")) + wpa = 2; + else if (strstr(reply, "WPA")) + wpa = 1; + } + + int auth = AUTH_NONE, encr = 0; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "WPA-EAP")) + auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; + else if (strstr(reply, "WPA-PSK")) + auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; + else if (strstr(reply, "IEEE8021X")) { + auth = AUTH_IEEE8021X; + encr = 1; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP") && auth != AUTH_NONE) + encr = 1; + else if (strstr(reply, "TKIP")) + encr = 0; + else if (strstr(reply, "WEP")) + encr = 1; + else + encr = 0; + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + pskEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + pskEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + identityEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + passwordEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + passwordEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + cacertEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { + reply[reply_len] = '\0'; + for (i = 0; i < eapSelect->count(); i++) { + if (eapSelect->text(i).compare(reply) == 0) { + eapSelect->setCurrentItem(i); + break; + } + } + } + + for (i = 0; i < 4; i++) { + QLineEdit *wepEdit; + switch (i) { + default: + case 0: + wepEdit = wep0Edit; + break; + case 1: + wepEdit = wep1Edit; + break; + case 2: + wepEdit = wep2Edit; + break; + case 3: + wepEdit = wep3Edit; + break; + } + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { + reply[reply_len] = '\0'; + switch (atoi(reply)) { + case 0: + wep0Radio->setChecked(true); + break; + case 1: + wep1Radio->setChecked(true); + break; + case 2: + wep2Radio->setChecked(true); + break; + case 3: + wep3Radio->setChecked(true); + break; + } + } + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + wepEnabled(encr == 1); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information(this, "wpa_gui", + "This will permanently remove the network\n" + "from the configuration. Do you really want\n" + "to remove this network?", "Yes", "No") != 0) + return; + + snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + "Failed to remove network from wpa_supplicant\n" + "configuration."); + } else { + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + } + + close(); +} + + +void NetworkConfig::newNetwork() +{ + new_network = true; + getEapCapa(); +} + + +void NetworkConfig::getEapCapa() +{ + char reply[256]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + QString res(reply); + QStringList types = QStringList::split(QChar(' '), res); + eapSelect->insertStringList(types); +} diff --git a/wpa_supplicant/wpa_gui/scanresults.ui b/wpa_supplicant/wpa_gui/scanresults.ui new file mode 100644 index 0000000000000..66c8b4b76f329 --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui @@ -0,0 +1,179 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ScanResults</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ScanResults</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>225</height> + </rect> + </property> + <property name="caption"> + <string>Scan results</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>SSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>BSSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>frequency</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>signal</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>flags</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>scanResultsView</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>scanRequest()</slot> + </connection> + <connection> + <sender>scanResultsView</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>ScanResults</receiver> + <slot>bssSelected(QListViewItem*)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">scanresults.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">QTimer *timer;</variable> +</variables> +<slots> + <slot>setWpaGui( WpaGui * _wpagui )</slot> + <slot>updateResults()</slot> + <slot>scanRequest()</slot> + <slot>getResults()</slot> + <slot>bssSelected( QListViewItem * sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/scanresults.ui.h b/wpa_supplicant/wpa_gui/scanresults.ui.h new file mode 100644 index 0000000000000..530d2e6a495c0 --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void ScanResults::init() +{ + wpagui = NULL; +} + + +void ScanResults::destroy() +{ + delete timer; +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(getResults())); + timer->start(10000, FALSE); +} + + +void ScanResults::updateResults() +{ + char reply[8192]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("SCAN_RESULTS", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + scanResultsView->clear(); + + QString res(reply); + QStringList lines = QStringList::split(QChar('\n'), res); + bool first = true; + for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++) { + if (first) { + first = false; + continue; + } + + QStringList cols = QStringList::split(QChar('\t'), *it, true); + QString ssid, bssid, freq, signal, flags; + bssid = cols.count() > 0 ? cols[0] : ""; + freq = cols.count() > 1 ? cols[1] : ""; + signal = cols.count() > 2 ? cols[2] : ""; + flags = cols.count() > 3 ? cols[3] : ""; + ssid = cols.count() > 4 ? cols[4] : ""; + new Q3ListViewItem(scanResultsView, ssid, bssid, freq, signal, flags); + } +} + + +void ScanResults::scanRequest() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) + return; + + wpagui->ctrlRequest("SCAN", reply, &reply_len); +} + + +void ScanResults::getResults() +{ + updateResults(); +} + + + + +void ScanResults::bssSelected( Q3ListViewItem * sel ) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); +} diff --git a/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling new file mode 100755 index 0000000000000..e173b00137814 --- /dev/null +++ b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling @@ -0,0 +1,11 @@ +#!/bin/sh + +# qmake seems to be forcing include and lib paths from the original build +# and I have no idea how to change these. For now, just override the +# directories in the Makefile.Release file after qmake run. + +qmake -spec /q/jm/qt4-win/4.0.0/mkspecs/win32-g++ wpa_gui.pro -o Makefile +cat Makefile.Release | + sed s%qt4/lib%qt4-win/4.0.0/lib%g | + sed s%qt4/include%qt4-win/4.0.0/include%g > tmp.Makefile.Release && +mv -f tmp.Makefile.Release Makefile.Release diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui b/wpa_supplicant/wpa_gui/userdatarequest.ui new file mode 100644 index 0000000000000..c3d545fa620a7 --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui @@ -0,0 +1,163 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>UserDataRequest</class> +<widget class="QDialog"> + <property name="name"> + <cstring>UserDataRequest</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="caption"> + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryInfo</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryField</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>queryEdit</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout27</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>queryEdit</sender> + <signal>returnPressed()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">userdatarequest.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int networkid;</variable> + <variable access="private">QString field;</variable> +</variables> +<slots> + <slot>sendReply()</slot> +</slots> +<functions> + <function specifier="non virtual" returnType="int">setParams( WpaGui * _wpagui, const char * reqMsg )</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui.h b/wpa_supplicant/wpa_gui/userdatarequest.ui.h new file mode 100644 index 0000000000000..66d4478d23e24 --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) +{ + char *tmp, *pos, *pos2; + wpagui = _wpagui; + tmp = strdup(reqMsg); + if (tmp == NULL) + return -1; + pos = strchr(tmp, '-'); + if (pos == NULL) { + free(tmp); + return -1; + } + *pos++ = '\0'; + field = tmp; + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + free(tmp); + return -1; + } + *pos2++ = '\0'; + + networkid = atoi(pos); + queryInfo->setText(pos2); + if (strcmp(tmp, "PASSWORD") == 0) { + queryField->setText("Password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText("New password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText("Identity: "); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText("Private key passphrase: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else + queryField->setText(field + ":"); + free(tmp); + + return 0; +} + + +void UserDataRequest::sendReply() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) { + reject(); + return; + } + + QString cmd = QString(WPA_CTRL_RSP) + field + '-' + + QString::number(networkid) + ':' + + queryEdit->text(); + wpagui->ctrlRequest(cmd.ascii(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui/wpa_gui.pro b/wpa_supplicant/wpa_gui/wpa_gui.pro new file mode 100644 index 0000000000000..6363db92287ce --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpa_gui.pro @@ -0,0 +1,50 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt warn_on release + +DEFINES += CONFIG_CTRL_IFACE + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src/utils ../../src/common + +HEADERS += wpamsg.h + +SOURCES += main.cpp \ + ../../src/common/wpa_ctrl.c + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +qtver = $$[QT_VERSION] +isEmpty( qtver ) { + message(Compiling for Qt 3.x) + DEFINES += Q3ListViewItem=QListViewItem +} else { + message(Compiling for Qt $$qtver) + QT += qt3support + CONFIG += uic3 +} diff --git a/wpa_supplicant/wpa_gui/wpagui.ui b/wpa_supplicant/wpa_gui/wpagui.ui new file mode 100644 index 0000000000000..01666a363479b --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui @@ -0,0 +1,471 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WpaGui</class> +<widget class="QMainWindow"> + <property name="name"> + <cstring>WpaGui</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>279</width> + <height>308</height> + </rect> + </property> + <property name="caption"> + <string>wpa_gui</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel16</cstring> + </property> + <property name="text"> + <string>Adapter:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>adapterSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Network:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>networkSelect</cstring> + </property> + </widget> + <widget class="QFrame" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Status:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Last message:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Authentication:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Encryption:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>SSID:</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>BSSID:</string> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>IP address:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textStatus</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLastMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>textAuthentication</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>textEncryption</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="4" column="1"> + <property name="name"> + <cstring>textSsid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="1"> + <property name="name"> + <cstring>textBssid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="6" column="1"> + <property name="name"> + <cstring>textIpAddress</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>connectButton</cstring> + </property> + <property name="text"> + <string>Connect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>disconnectButton</cstring> + </property> + <property name="text"> + <string>Disconnect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="3"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + </grid> +</widget> +<menubar> + <property name="name"> + <cstring>MenuBar</cstring> + </property> + <item text="&File" name="fileMenu"> + <separator/> + <action name="fileEventHistoryAction"/> + <action name="fileAdd_NetworkAction"/> + <action name="fileEdit_networkAction"/> + <separator/> + <action name="fileExitAction"/> + </item> + <item text="&Help" name="helpMenu"> + <action name="helpContentsAction"/> + <action name="helpIndexAction"/> + <separator/> + <action name="helpAboutAction"/> + </item> +</menubar> +<toolbars> +</toolbars> +<actions> + <action> + <property name="name"> + <cstring>fileExitAction</cstring> + </property> + <property name="text"> + <string>Exit</string> + </property> + <property name="menuText"> + <string>E&xit</string> + </property> + <property name="accel"> + <string>Ctrl+Q</string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpContentsAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Contents</string> + </property> + <property name="menuText"> + <string>&Contents...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpIndexAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Index</string> + </property> + <property name="menuText"> + <string>&Index...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpAboutAction</cstring> + </property> + <property name="text"> + <string>About</string> + </property> + <property name="menuText"> + <string>&About</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEventHistoryAction</cstring> + </property> + <property name="text"> + <string>Event History</string> + </property> + <property name="menuText"> + <string>Event &History</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileAdd_NetworkAction</cstring> + </property> + <property name="text"> + <string>Add Network</string> + </property> + <property name="menuText"> + <string>&Add Network</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEdit_networkAction</cstring> + </property> + <property name="text"> + <string>Edit Network</string> + </property> + <property name="menuText"> + <string>&Edit Network</string> + </property> + </action> +</actions> +<connections> + <connection> + <sender>helpIndexAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpIndex()</slot> + </connection> + <connection> + <sender>helpContentsAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpContents()</slot> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>fileExitAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>disconnectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>disconnect()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>scan()</slot> + </connection> + <connection> + <sender>connectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>connectB()</slot> + </connection> + <connection> + <sender>fileEventHistoryAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>eventHistory()</slot> + </connection> + <connection> + <sender>networkSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectNetwork(const QString&)</slot> + </connection> + <connection> + <sender>fileEdit_networkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>editNetwork()</slot> + </connection> + <connection> + <sender>fileAdd_NetworkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>adapterSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectAdapter(const QString&)</slot> + </connection> +</connections> +<includes> + <include location="global" impldecl="in declaration">qtimer.h</include> + <include location="global" impldecl="in declaration">qsocketnotifier.h</include> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in declaration">eventhistory.h</include> + <include location="local" impldecl="in declaration">scanresults.h</include> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="global" impldecl="in implementation">dirent.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="global" impldecl="in implementation">qapplication.h</include> + <include location="local" impldecl="in implementation">userdatarequest.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">wpagui.ui.h</include> +</includes> +<forwards> + <forward>class UserDataRequest;</forward> +</forwards> +<variables> + <variable access="private">ScanResults *scanres;</variable> + <variable access="private">bool networkMayHaveChanged;</variable> + <variable access="private">char *ctrl_iface;</variable> + <variable access="private">EventHistory *eh;</variable> + <variable access="private">struct wpa_ctrl *ctrl_conn;</variable> + <variable access="private">QSocketNotifier *msgNotifier;</variable> + <variable access="private">QTimer *timer;</variable> + <variable access="private">int pingsToStatusUpdate;</variable> + <variable access="private">WpaMsgList msgs;</variable> + <variable access="private">char *ctrl_iface_dir;</variable> + <variable access="private">struct wpa_ctrl *monitor_conn;</variable> + <variable access="private">UserDataRequest *udr;</variable> +</variables> +<slots> + <slot>parse_argv()</slot> + <slot>updateStatus()</slot> + <slot>updateNetworks()</slot> + <slot>helpIndex()</slot> + <slot>helpContents()</slot> + <slot>helpAbout()</slot> + <slot>disconnect()</slot> + <slot>scan()</slot> + <slot>eventHistory()</slot> + <slot>ping()</slot> + <slot>processMsg( char * msg )</slot> + <slot>processCtrlReq( const char * req )</slot> + <slot>receiveMsgs()</slot> + <slot>connectB()</slot> + <slot>selectNetwork( const QString & sel )</slot> + <slot>editNetwork()</slot> + <slot>addNetwork()</slot> + <slot>selectAdapter( const QString & sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> + <function access="private" specifier="non virtual" returnType="int">openCtrlConnection( const char * ifname )</function> + <function returnType="int">ctrlRequest( const char * cmd, char * buf, size_t * buflen )</function> + <function>triggerUpdate()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/wpagui.ui.h b/wpa_supplicant/wpa_gui/wpagui.ui.h new file mode 100644 index 0000000000000..678ff1be354b4 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui.h @@ -0,0 +1,730 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#include <stdlib.h> + +void WpaGui::init() +{ + eh = NULL; + scanres = NULL; + udr = NULL; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + parse_argv(); + + textStatus->setText("connecting to wpa_supplicant"); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->start(1000, FALSE); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to wpa_supplicant.\n"); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::destroy() +{ + delete msgNotifier; + + if (monitor_conn) { + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::parse_argv() +{ + int c; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + } + } +} + + +int WpaGui::openCtrlConnection(const char *ifname) +{ + char *cfile; + int flen; + char buf[2048], *pos, *pos2; + size_t len; + + if (ifname) { + if (ifname != ctrl_iface) { + free(ctrl_iface); + ctrl_iface = strdup(ifname); + } + } else { +#ifdef CONFIG_CTRL_IFACE_UDP + free(ctrl_iface); + ctrl_iface = strdup("udp"); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", dent->d_name); + ctrl_iface = strdup(dent->d_name); + break; + } + closedir(dir); + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + + free(ctrl_iface); + ctrl_iface = NULL; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl) { + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL); + if (ret >= 0) { + buf[len] = '\0'; + pos = strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ctrl_iface = strdup(buf); + } + wpa_ctrl_close(ctrl); + } +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + } + + if (ctrl_iface == NULL) + return -1; + +#ifdef CONFIG_CTRL_IFACE_UNIX + flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); +#else /* CONFIG_CTRL_IFACE_UNIX */ + flen = strlen(ctrl_iface) + 1; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s", ctrl_iface); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn) { + delete msgNotifier; + msgNotifier = NULL; + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + printf("Trying to connect to '%s'\n", cfile); + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + free(cfile); + return -1; + } + monitor_conn = wpa_ctrl_open(cfile); + free(cfile); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + return -1; + } + if (wpa_ctrl_attach(monitor_conn)) { + printf("Failed to attach to wpa_supplicant\n"); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); +#endif + + adapterSelect->clear(); + adapterSelect->insertItem(ctrl_iface); + adapterSelect->setCurrentItem(0); + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= 0) { + buf[len] = '\0'; + pos = buf; + while (*pos) { + pos2 = strchr(pos, '\n'); + if (pos2) + *pos2 = '\0'; + if (strcmp(pos, ctrl_iface) != 0) + adapterSelect->insertItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + return 0; +} + + +static void wpa_gui_msg_cb(char *msg, size_t) +{ + /* This should not happen anymore since two control connections are used. */ + printf("missed message: %s\n", msg); +} + + +int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen) +{ + int ret; + + if (ctrl_conn == NULL) + return -3; + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, + wpa_gui_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + } + + return ret; +} + + +void WpaGui::updateStatus() +{ + char buf[2048], *start, *end, *pos; + size_t len; + + pingsToStatusUpdate = 10; + + len = sizeof(buf) - 1; + if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) { + textStatus->setText("Could not get status from wpa_supplicant"); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + return; + } + + buf[len] = '\0'; + + bool auth_updated = false, ssid_updated = false; + bool bssid_updated = false, ipaddr_updated = false; + bool status_updated = false; + char *pairwise_cipher = NULL, *group_cipher = NULL; + + start = buf; + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + pos = strchr(start, '='); + if (pos) { + *pos++ = '\0'; + if (strcmp(start, "bssid") == 0) { + bssid_updated = true; + textBssid->setText(pos); + } else if (strcmp(start, "ssid") == 0) { + ssid_updated = true; + textSsid->setText(pos); + } else if (strcmp(start, "ip_address") == 0) { + ipaddr_updated = true; + textIpAddress->setText(pos); + } else if (strcmp(start, "wpa_state") == 0) { + status_updated = true; + textStatus->setText(pos); + } else if (strcmp(start, "key_mgmt") == 0) { + auth_updated = true; + textAuthentication->setText(pos); + /* TODO: could add EAP status to this */ + } else if (strcmp(start, "pairwise_cipher") == 0) { + pairwise_cipher = pos; + } else if (strcmp(start, "group_cipher") == 0) { + group_cipher = pos; + } + } + + if (last) + break; + start = end + 1; + } + + if (pairwise_cipher || group_cipher) { + QString encr; + if (pairwise_cipher && group_cipher && + strcmp(pairwise_cipher, group_cipher) != 0) { + encr.append(pairwise_cipher); + encr.append(" + "); + encr.append(group_cipher); + } else if (pairwise_cipher) { + encr.append(pairwise_cipher); + } else { + encr.append(group_cipher); + encr.append(" [group key only]"); + } + textEncryption->setText(encr); + } else + textEncryption->clear(); + + if (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + bool selected = false; + + if (!networkMayHaveChanged) + return; + + networkSelect->clear(); + + if (ctrl_conn == NULL) + return; + + len = sizeof(buf) - 1; + if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->insertItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentItem(networkSelect->count() - 1); + selected = true; + } else if (first_active < 0 && strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (!selected && first_active >= 0) + networkSelect->setCurrentItem(first_active); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2008,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\n" + "\n" + "This product includes software developed\n" + "by the OpenSSL Project for use in the\n" + "OpenSSL Toolkit (http://www.openssl.org/)\n"); +} + + +void WpaGui::disconnect() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("DISCONNECT", reply, &reply_len); +} + + +void WpaGui::scan() +{ + if (scanres) { + scanres->close(); + delete scanres; + } + + scanres = new ScanResults(); + if (scanres == NULL) + return; + scanres->setWpaGui(this); + scanres->show(); + scanres->exec(); +} + + +void WpaGui::eventHistory() +{ + if (eh) { + eh->close(); + delete eh; + } + + eh = new EventHistory(); + if (eh == NULL) + return; + eh->addEvents(msgs); + eh->show(); + eh->exec(); +} + + +void WpaGui::ping() +{ + char buf[10]; + size_t len; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + /* + * QSocketNotifier cannot be used with Windows named pipes, so use a timer + * to check for received messages for now. This could be optimized be doing + * something specific to named pipes or Windows events, but it is not clear + * what would be the best way of doing that in Qt. + */ + receiveMsgs(); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (scanres && !scanres->isVisible()) { + delete scanres; + scanres = NULL; + } + + if (eh && !eh->isVisible()) { + delete eh; + eh = NULL; + } + + if (udr && !udr->isVisible()) { + delete udr; + udr = NULL; + } + + len = sizeof(buf) - 1; + if (ctrlRequest("PING", buf, &len) < 0) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } +} + + +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +void WpaGui::processMsg(char *msg) +{ + char *pos = msg, *pos2; + int priority = 2; + + if (*pos == '<') { + /* skip priority */ + pos++; + priority = atoi(pos); + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + WpaMsg wm(pos, priority); + if (eh) + eh->addEvent(wm); + msgs.append(wm); + while (msgs.count() > 100) + msgs.pop_front(); + + /* Update last message with truncated version of the event */ + if (strncmp(pos, "CTRL-", 5) == 0) { + pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); + if (pos2) + pos2++; + else + pos2 = pos; + } else + pos2 = pos; + QString lastmsg = pos2; + lastmsg.truncate(40); + textLastMessage->setText(lastmsg); + + pingsToStatusUpdate = 0; + networkMayHaveChanged = true; + + if (str_match(pos, WPA_CTRL_REQ)) + processCtrlReq(pos + strlen(WPA_CTRL_REQ)); +} + + +void WpaGui::processCtrlReq(const char *req) +{ + if (udr) { + udr->close(); + delete udr; + } + udr = new UserDataRequest(); + if (udr == NULL) + return; + if (udr->setParams(this, req) < 0) { + delete udr; + udr = NULL; + return; + } + udr->show(); + udr->exec(); +} + + +void WpaGui::receiveMsgs() +{ + char buf[256]; + size_t len; + + while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { + len = sizeof(buf) - 1; + if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { + buf[len] = '\0'; + processMsg(buf); + } + } +} + + +void WpaGui::connectB() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("REASSOCIATE", reply, &reply_len); +} + + +void WpaGui::selectNetwork( const QString &sel ) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + int pos = cmd.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", cmd.ascii()); + return; + } + cmd.truncate(pos); + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.ascii(), reply, &reply_len); +} + + +void WpaGui::editNetwork() +{ + QString sel(networkSelect->currentText()); + int pos = sel.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", sel.ascii()); + return; + } + sel.truncate(pos); + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + nc->paramsFromConfig(sel.toInt()); + nc->show(); + nc->exec(); +} + + +void WpaGui::triggerUpdate() +{ + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::addNetwork() +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + nc->newNetwork(); + nc->show(); + nc->exec(); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.ascii()) < 0) + printf("Failed to open control connection to wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} diff --git a/wpa_supplicant/wpa_gui/wpamsg.h b/wpa_supplicant/wpa_gui/wpamsg.h new file mode 100644 index 0000000000000..f3fce06978c77 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpamsg.h @@ -0,0 +1,34 @@ +#ifndef WPAMSG_H +#define WPAMSG_H + +class WpaMsg; + +#if QT_VERSION >= 0x040000 +#include <QDateTime> +#include <QLinkedList> +typedef QLinkedList<WpaMsg> WpaMsgList; +#else +#include <qdatetime.h> +typedef QValueList<WpaMsg> WpaMsgList; +#endif + +class WpaMsg { +public: + WpaMsg() {} + WpaMsg(const QString &_msg, int _priority = 2) + : msg(_msg), priority(_priority) + { + timestamp = QDateTime::currentDateTime(); + } + + QString getMsg() const { return msg; } + int getPriority() const { return priority; } + QDateTime getTimestamp() const { return timestamp; } + +private: + QString msg; + int priority; + QDateTime timestamp; +}; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c new file mode 100644 index 0000000000000..96b0c32c2cee9 --- /dev/null +++ b/wpa_supplicant/wpa_passphrase.c @@ -0,0 +1,73 @@ +/* + * WPA Supplicant - ASCII passphrase to WPA PSK tool + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" + + +int main(int argc, char *argv[]) +{ + unsigned char psk[32]; + int i; + char *ssid, *passphrase, buf[64], *pos; + + if (argc < 2) { + printf("usage: wpa_passphrase <ssid> [passphrase]\n" + "\nIf passphrase is left out, it will be read from " + "stdin\n"); + return 1; + } + + ssid = argv[1]; + + if (argc > 2) { + passphrase = argv[2]; + } else { + printf("# reading passphrase from stdin\n"); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("Failed to read passphrase\n"); + return 1; + } + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + passphrase = buf; + } + + if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) { + printf("Passphrase must be 8..63 characters\n"); + return 1; + } + + pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32); + + printf("network={\n"); + printf("\tssid=\"%s\"\n", ssid); + printf("\t#psk=\"%s\"\n", passphrase); + printf("\tpsk="); + for (i = 0; i < 32; i++) + printf("%02x", psk[i]); + printf("\n"); + printf("}\n"); + + return 0; +} diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c new file mode 100644 index 0000000000000..4a27125d9c87a --- /dev/null +++ b/wpa_supplicant/wpa_priv.c @@ -0,0 +1,1220 @@ +/* + * WPA Supplicant / privileged helper program + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ +#include <sys/un.h> +#include <sys/stat.h> + +#include "common.h" +#include "eloop.h" +#include "version.h" +#include "drivers/driver.h" +#include "l2_packet/l2_packet.h" +#include "privsep_commands.h" +#include "ieee802_11_defs.h" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif + + +struct wpa_priv_interface { + struct wpa_priv_interface *next; + char *driver_name; + char *ifname; + char *sock_name; + int fd; + + struct wpa_driver_ops *driver; + void *drv_priv; + struct sockaddr_un drv_addr; + int wpas_registered; + + /* TODO: add support for multiple l2 connections */ + struct l2_packet_data *l2; + struct sockaddr_un l2_addr; +}; + + +static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance"); + if (iface->driver->set_wpa) + iface->driver->set_wpa(iface->drv_priv, 0); + if (iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + iface->drv_priv = NULL; + iface->wpas_registered = 0; + } + + if (iface->l2) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " + "instance"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } + + if (iface->driver->init == NULL) + return; + + iface->drv_priv = iface->driver->init(iface, iface->ifname); + if (iface->drv_priv == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper"); + return; + } + + wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface " + "'%s'", iface->driver_name, iface->ifname); + + os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr)); + iface->wpas_registered = 1; + + if (iface->driver->set_param && + iface->driver->set_param(iface->drv_priv, NULL) < 0) { + wpa_printf(MSG_ERROR, "Driver interface rejected param"); + } + + if (iface->driver->set_wpa) + iface->driver->set_wpa(iface->drv_priv, 1); +} + + +static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv) { + if (iface->driver->set_wpa) + iface->driver->set_wpa(iface->drv_priv, 0); + if (iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + iface->drv_priv = NULL; + iface->wpas_registered = 0; + } +} + + +static void wpa_priv_cmd_set_wpa(struct wpa_priv_interface *iface, + char *buf, size_t len) +{ + if (iface->drv_priv == NULL || len != sizeof(int)) + return; + + if (iface->driver->set_wpa) + iface->driver->set_wpa(iface->drv_priv, *((int *) buf)); +} + + +static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface, + char *buf, size_t len) +{ + if (iface->drv_priv == NULL) + return; + + if (iface->driver->scan) + iface->driver->scan(iface->drv_priv, len ? (u8 *) buf : NULL, + len); +} + + +static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + struct wpa_scan_results *res; + u8 *buf = NULL, *pos, *end; + int val; + size_t i; + + res = iface->driver->get_scan_results2(iface->drv_priv); + if (res == NULL) + goto fail; + + buf = os_malloc(60000); + if (buf == NULL) + goto fail; + pos = buf; + end = buf + 60000; + val = res->num; + os_memcpy(pos, &val, sizeof(int)); + pos += sizeof(int); + + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + val = sizeof(*r) + r->ie_len; + if (end - pos < (int) sizeof(int) + val) + break; + os_memcpy(pos, &val, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, r, val); + pos += val; + } + + sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, + sizeof(*from)); + + os_free(buf); + os_free(res); + return; + +fail: + os_free(buf); + os_free(res); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_send_old_scan_results(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ +#define SCAN_AP_LIMIT 128 + int i, res, val; + struct wpa_scan_result *results = NULL; + u8 *buf = NULL, *pos, *end; + struct wpa_scan_res nres; + + results = os_malloc(SCAN_AP_LIMIT * sizeof(*results)); + if (results == NULL) + goto fail; + + res = iface->driver->get_scan_results(iface->drv_priv, results, + SCAN_AP_LIMIT); + if (res < 0 || res > SCAN_AP_LIMIT) + goto fail; + + buf = os_malloc(60000); + if (buf == NULL) + goto fail; + pos = buf; + end = buf + 60000; + os_memcpy(pos, &res, sizeof(int)); + pos += sizeof(int); + + os_memset(&nres, 0, sizeof(nres)); + for (i = 0; i < res; i++) { + struct wpa_scan_result *r = &results[i]; + size_t ie_len; + + ie_len = 2 + r->ssid_len + r->rsn_ie_len + r->wpa_ie_len; + if (r->maxrate) + ie_len += 3; + if (r->mdie_present) + ie_len += 5; + + val = sizeof(nres) + ie_len; + if (end - pos < (int) sizeof(int) + val) + break; + os_memcpy(pos, &val, sizeof(int)); + pos += sizeof(int); + + os_memcpy(nres.bssid, r->bssid, ETH_ALEN); + nres.freq = r->freq; + nres.caps = r->caps; + nres.qual = r->qual; + nres.noise = r->noise; + nres.level = r->level; + nres.tsf = r->tsf; + nres.ie_len = ie_len; + + os_memcpy(pos, &nres, sizeof(nres)); + pos += sizeof(nres); + + /* SSID IE */ + *pos++ = WLAN_EID_SSID; + *pos++ = r->ssid_len; + os_memcpy(pos, r->ssid, r->ssid_len); + pos += r->ssid_len; + + if (r->maxrate) { + /* Fake Supported Rate IE to include max rate */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = r->maxrate; + } + + if (r->rsn_ie_len) { + os_memcpy(pos, r->rsn_ie, r->rsn_ie_len); + pos += r->rsn_ie_len; + } + + if (r->mdie_present) { + os_memcpy(pos, r->mdie, 5); + pos += 5; + } + + if (r->wpa_ie_len) { + os_memcpy(pos, r->wpa_ie, r->wpa_ie_len); + pos += r->wpa_ie_len; + } + } + + sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, + sizeof(*from)); + + os_free(buf); + os_free(results); + return; + +fail: + os_free(buf); + os_free(results); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv == NULL) + return; + + if (iface->driver->get_scan_results2) + wpa_priv_get_scan_results2(iface, from); + else if (iface->driver->get_scan_results) + wpa_priv_send_old_scan_results(iface, from); + else + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, + sizeof(*from)); +} + + +static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct wpa_driver_associate_params params; + struct privsep_cmd_associate *assoc; + u8 *bssid; + int res; + + if (iface->drv_priv == NULL || iface->driver->associate == NULL) + return; + + if (len < sizeof(*assoc)) { + wpa_printf(MSG_DEBUG, "Invalid association request"); + return; + } + + assoc = buf; + if (sizeof(*assoc) + assoc->wpa_ie_len > len) { + wpa_printf(MSG_DEBUG, "Association request overflow"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + bssid = assoc->bssid; + if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5]) + params.bssid = bssid; + params.ssid = assoc->ssid; + if (assoc->ssid_len > 32) + return; + params.ssid_len = assoc->ssid_len; + params.freq = assoc->freq; + if (assoc->wpa_ie_len) { + params.wpa_ie = (u8 *) (assoc + 1); + params.wpa_ie_len = assoc->wpa_ie_len; + } + params.pairwise_suite = assoc->pairwise_suite; + params.group_suite = assoc->group_suite; + params.key_mgmt_suite = assoc->key_mgmt_suite; + params.auth_alg = assoc->auth_alg; + params.mode = assoc->mode; + + res = iface->driver->associate(iface->drv_priv, ¶ms); + wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res); +} + + +static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + u8 bssid[ETH_ALEN]; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_bssid == NULL || + iface->driver->get_bssid(iface->drv_priv, bssid) < 0) + goto fail; + + sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + u8 ssid[sizeof(int) + 32]; + int res; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_ssid == NULL) + goto fail; + + res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); + if (res < 0 || res > 32) + goto fail; + os_memcpy(ssid, &res, sizeof(int)); + + sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct privsep_cmd_set_key *params; + int res; + + if (iface->drv_priv == NULL || iface->driver->set_key == NULL) + return; + + if (len != sizeof(*params)) { + wpa_printf(MSG_DEBUG, "Invalid set_key request"); + return; + } + + params = buf; + + res = iface->driver->set_key(iface->drv_priv, params->alg, + params->addr, params->key_idx, + params->set_tx, + params->seq_len ? params->seq : NULL, + params->seq_len, + params->key_len ? params->key : NULL, + params->key_len); + wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res); +} + + +static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + struct wpa_driver_capa capa; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_capa == NULL || + iface->driver->get_capa(iface->drv_priv, &capa) < 0) + goto fail; + + sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct wpa_priv_interface *iface = ctx; + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = (u8 *) src_addr; + io[0].iov_len = ETH_ALEN; + io[1].iov_base = (u8 *) buf; + io[1].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + msg.msg_name = &iface->l2_addr; + msg.msg_namelen = sizeof(iface->l2_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) { + perror("sendmsg(l2 rx)"); + } +} + + +static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, + struct sockaddr_un *from, + void *buf, size_t len) +{ + int *reg_cmd = buf; + u8 own_addr[ETH_ALEN]; + int res; + u16 proto; + + if (len != 2 * sizeof(int)) { + wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu", + (unsigned long) len); + return; + } + + proto = reg_cmd[0]; + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " + "ethertype 0x%x", proto); + return; + } + + if (iface->l2) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " + "instance"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } + + os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr)); + + iface->l2 = l2_packet_init(iface->ifname, NULL, proto, + wpa_priv_l2_rx, iface, reg_cmd[1]); + if (iface->l2 == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet " + "instance for protocol %d", proto); + return; + } + + if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) { + wpa_printf(MSG_DEBUG, "Failed to get own address from " + "l2_packet"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + return; + } + + res = sendto(iface->fd, own_addr, ETH_ALEN, 0, + (struct sockaddr *) from, sizeof(*from)); + wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res); +} + + +static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->l2) { + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } +} + + +static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->l2) + l2_packet_notify_auth_start(iface->l2); +} + + +static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface, + struct sockaddr_un *from, + void *buf, size_t len) +{ + u8 *dst_addr; + u16 proto; + int res; + + if (iface->l2 == NULL) + return; + + if (len < ETH_ALEN + 2) { + wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)", + (unsigned long) len); + return; + } + + dst_addr = buf; + os_memcpy(&proto, buf + ETH_ALEN, 2); + + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype " + "0x%x", proto); + return; + } + + res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2, + len - ETH_ALEN - 2); + wpa_printf(MSG_DEBUG, "L2 send: res=%d", res); +} + + +static void wpa_priv_cmd_set_mode(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + if (iface->drv_priv == NULL || iface->driver->set_mode == NULL || + len != sizeof(int)) + return; + + iface->driver->set_mode(iface->drv_priv, *((int *) buf)); +} + + +static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface, + char *buf) +{ + if (iface->drv_priv == NULL || iface->driver->set_country == NULL || + *buf == '\0') + return; + + iface->driver->set_country(iface->drv_priv, buf); +} + + +static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_priv_interface *iface = eloop_ctx; + char buf[2000], *pos; + void *cmd_buf; + size_t cmd_len; + int res, cmd; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("recvfrom"); + return; + } + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res); + return; + } + + os_memcpy(&cmd, buf, sizeof(int)); + wpa_printf(MSG_DEBUG, "Command %d for interface %s", + cmd, iface->ifname); + cmd_buf = &buf[sizeof(int)]; + cmd_len = res - sizeof(int); + + switch (cmd) { + case PRIVSEP_CMD_REGISTER: + wpa_priv_cmd_register(iface, &from); + break; + case PRIVSEP_CMD_UNREGISTER: + wpa_priv_cmd_unregister(iface, &from); + break; + case PRIVSEP_CMD_SET_WPA: + wpa_priv_cmd_set_wpa(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_SCAN: + wpa_priv_cmd_scan(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_SCAN_RESULTS: + wpa_priv_cmd_get_scan_results(iface, &from); + break; + case PRIVSEP_CMD_ASSOCIATE: + wpa_priv_cmd_associate(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_BSSID: + wpa_priv_cmd_get_bssid(iface, &from); + break; + case PRIVSEP_CMD_GET_SSID: + wpa_priv_cmd_get_ssid(iface, &from); + break; + case PRIVSEP_CMD_SET_KEY: + wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_CAPA: + wpa_priv_cmd_get_capa(iface, &from); + break; + case PRIVSEP_CMD_L2_REGISTER: + wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_L2_UNREGISTER: + wpa_priv_cmd_l2_unregister(iface, &from); + break; + case PRIVSEP_CMD_L2_NOTIFY_AUTH_START: + wpa_priv_cmd_l2_notify_auth_start(iface, &from); + break; + case PRIVSEP_CMD_L2_SEND: + wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_SET_MODE: + wpa_priv_cmd_set_mode(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_SET_COUNTRY: + pos = cmd_buf; + if (pos + cmd_len >= buf + sizeof(buf)) + break; + pos[cmd_len] = '\0'; + wpa_priv_cmd_set_country(iface, pos); + break; + } +} + + +static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) +{ + if (iface->drv_priv && iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + + if (iface->fd >= 0) { + eloop_unregister_read_sock(iface->fd); + close(iface->fd); + unlink(iface->sock_name); + } + + if (iface->l2) + l2_packet_deinit(iface->l2); + + os_free(iface->ifname); + os_free(iface->driver_name); + os_free(iface->sock_name); + os_free(iface); +} + + +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; + +static struct wpa_priv_interface * +wpa_priv_interface_init(const char *dir, const char *params) +{ + struct wpa_priv_interface *iface; + char *pos; + size_t len; + struct sockaddr_un addr; + int i; + + pos = os_strchr(params, ':'); + if (pos == NULL) + return NULL; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return NULL; + iface->fd = -1; + + len = pos - params; + iface->driver_name = os_malloc(len + 1); + if (iface->driver_name == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + os_memcpy(iface->driver_name, params, len); + iface->driver_name[len] = '\0'; + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + if (os_strcmp(iface->driver_name, + wpa_supplicant_drivers[i]->name) == 0) { + iface->driver = wpa_supplicant_drivers[i]; + break; + } + } + if (iface->driver == NULL) { + wpa_printf(MSG_ERROR, "Unsupported driver '%s'", + iface->driver_name); + wpa_priv_interface_deinit(iface); + return NULL; + } + + pos++; + iface->ifname = os_strdup(pos); + if (iface->ifname == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + len = os_strlen(dir) + 1 + os_strlen(iface->ifname); + iface->sock_name = os_malloc(len + 1); + if (iface->sock_name == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname); + if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (iface->fd < 0) { + perror("socket(PF_UNIX)"); + wpa_priv_interface_deinit(iface); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path)); + + if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(iface->fd, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "Socket exists, but does not " + "allow connections - assuming it was " + "leftover from forced program termination"); + if (unlink(iface->sock_name) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + iface->sock_name); + goto fail; + } + if (bind(iface->fd, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "socket '%s'", iface->sock_name); + } else { + wpa_printf(MSG_INFO, "Socket exists and seems to be " + "in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", iface->sock_name); + goto fail; + } + } + + if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + perror("chmod"); + goto fail; + } + + eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL); + + return iface; + +fail: + wpa_priv_interface_deinit(iface); + return NULL; +} + + +static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, + const void *data, size_t data_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &event; + io[0].iov_len = sizeof(event); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &iface->drv_addr; + msg.msg_namelen = sizeof(iface->drv_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) { + perror("sendmsg(wpas_socket)"); + return -1; + } + + return 0; +} + + +static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event, + union wpa_event_data *data) +{ + size_t buflen = 3 * sizeof(int); + u8 *buf, *pos; + int len; + + if (data) { + buflen += data->assoc_info.req_ies_len + + data->assoc_info.resp_ies_len + + data->assoc_info.beacon_ies_len; + } + + buf = os_malloc(buflen); + if (buf == NULL) + return; + + pos = buf; + + if (data && data->assoc_info.req_ies) { + len = data->assoc_info.req_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.req_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + if (data && data->assoc_info.resp_ies) { + len = data->assoc_info.resp_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.resp_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + if (data && data->assoc_info.beacon_ies) { + len = data->assoc_info.beacon_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.beacon_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + wpa_priv_send_event(iface, event, buf, buflen); + + os_free(buf); +} + + +static void wpa_priv_send_interface_status(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + int ievent; + size_t len, maxlen; + u8 *buf; + char *ifname; + + if (data == NULL) + return; + + ievent = data->interface_status.ievent; + maxlen = sizeof(data->interface_status.ifname); + ifname = data->interface_status.ifname; + for (len = 0; len < maxlen && ifname[len]; len++) + ; + + buf = os_malloc(sizeof(int) + len); + if (buf == NULL) + return; + + os_memcpy(buf, &ievent, sizeof(int)); + os_memcpy(buf + sizeof(int), ifname, len); + + wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS, + buf, sizeof(int) + len); + + os_free(buf); + +} + + +static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + size_t len; + u8 *buf, *pos; + + if (data == NULL || data->ft_ies.ies == NULL) + return; + + len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len; + buf = os_malloc(len); + if (buf == NULL) + return; + + pos = buf; + os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len); + + wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len); + + os_free(buf); + +} + + +void wpa_supplicant_event(void *ctx, wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_priv_interface *iface = ctx; + + wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event); + + if (!iface->wpas_registered) { + wpa_printf(MSG_DEBUG, "Driver event received, but " + "wpa_supplicant not registered"); + return; + } + + switch (event) { + case EVENT_ASSOC: + wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data); + break; + case EVENT_DISASSOC: + wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0); + break; + case EVENT_ASSOCINFO: + if (data == NULL) + return; + wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data); + break; + case EVENT_MICHAEL_MIC_FAILURE: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, + &data->michael_mic_failure.unicast, + sizeof(int)); + break; + case EVENT_SCAN_RESULTS: + wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL, + 0); + break; + case EVENT_INTERFACE_STATUS: + wpa_priv_send_interface_status(iface, data); + break; + case EVENT_PMKID_CANDIDATE: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE, + &data->pmkid_candidate, + sizeof(struct pmkid_candidate)); + break; + case EVENT_STKSTART: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART, + &data->stkstart.peer, ETH_ALEN); + break; + case EVENT_FT_RESPONSE: + wpa_priv_send_ft_response(iface, data); + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO", + event); + break; + } +} + + +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_priv_interface *iface = ctx; + struct msghdr msg; + struct iovec io[3]; + int event = PRIVSEP_EVENT_RX_EAPOL; + + wpa_printf(MSG_DEBUG, "RX EAPOL from driver"); + io[0].iov_base = &event; + io[0].iov_len = sizeof(event); + io[1].iov_base = (u8 *) src_addr; + io[1].iov_len = ETH_ALEN; + io[2].iov_base = (u8 *) buf; + io[2].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &iface->drv_addr; + msg.msg_namelen = sizeof(iface->drv_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) + perror("sendmsg(wpas_socket)"); +} + + +#ifdef CONFIG_CLIENT_MLME +void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct wpa_priv_interface *iface = ctx; + struct msghdr msg; + struct iovec io[3]; + int event = PRIVSEP_EVENT_STA_RX; + + wpa_printf(MSG_DEBUG, "STA RX from driver"); + io[0].iov_base = &event; + io[0].iov_len = sizeof(event); + io[1].iov_base = (u8 *) rx_status; + io[1].iov_len = sizeof(*rx_status); + io[2].iov_base = (u8 *) buf; + io[2].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &iface->drv_addr; + msg.msg_namelen = sizeof(iface->drv_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) + perror("sendmsg(wpas_socket)"); +} +#endif /* CONFIG_CLIENT_MLME */ + + +static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx) +{ + wpa_printf(MSG_DEBUG, "wpa_priv termination requested"); + eloop_terminate(); +} + + +static void wpa_priv_fd_workaround(void) +{ +#ifdef __linux__ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +#endif /* __linux__ */ +} + + +static void usage(void) +{ + printf("wpa_priv v" VERSION_STR "\n" + "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and " + "contributors\n" + "\n" + "usage:\n" + " wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> " + "[driver:ifname ...]\n"); +} + + +extern int wpa_debug_level; + +int main(int argc, char *argv[]) +{ + int c, i; + int ret = -1; + char *pid_file = NULL; + int daemonize = 0; + char *ctrl_dir = "/var/run/wpa_priv"; + struct wpa_priv_interface *interfaces = NULL, *iface; + + if (os_program_init()) + return -1; + + wpa_priv_fd_workaround(); + + for (;;) { + c = getopt(argc, argv, "Bc:dP:"); + if (c < 0) + break; + switch (c) { + case 'B': + daemonize++; + break; + case 'c': + ctrl_dir = optarg; + break; + case 'd': + wpa_debug_level--; + break; + case 'P': + pid_file = os_rel2abs_path(optarg); + break; + default: + usage(); + goto out; + } + } + + if (optind >= argc) { + usage(); + goto out; + } + + wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir); + + if (eloop_init(NULL)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + goto out; + } + + for (i = optind; i < argc; i++) { + wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]); + iface = wpa_priv_interface_init(ctrl_dir, argv[i]); + if (iface == NULL) + goto out; + iface->next = interfaces; + interfaces = iface; + } + + if (daemonize && os_daemonize(pid_file)) + goto out; + + eloop_register_signal_terminate(wpa_priv_terminate, NULL); + eloop_run(); + + ret = 0; + +out: + iface = interfaces; + while (iface) { + struct wpa_priv_interface *prev = iface; + iface = iface->next; + wpa_priv_interface_deinit(prev); + } + + eloop_destroy(); + + os_daemonize_terminate(pid_file); + os_free(pid_file); + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c new file mode 100644 index 0000000000000..c1f95cb73ad2d --- /dev/null +++ b/wpa_supplicant/wpa_supplicant.c @@ -0,0 +1,2170 @@ +/* + * WPA Supplicant + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements functions for registering and unregistering + * %wpa_supplicant interfaces. In addition, this file contains number of + * functions for managing network connections. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eap_peer/eap.h" +#include "wpa.h" +#include "eloop.h" +#include "drivers/driver.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "ctrl_iface_dbus.h" +#include "pcsc_funcs.h" +#include "version.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_ctrl.h" +#include "mlme.h" +#include "ieee802_11_defs.h" +#include "blacklist.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" + +const char *wpa_supplicant_version = +"wpa_supplicant v" VERSION_STR "\n" +"Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors"; + +const char *wpa_supplicant_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n" +#ifdef EAP_TLS_OPENSSL +"\nThis product includes software developed by the OpenSSL Project\n" +"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" +#endif /* EAP_TLS_OPENSSL */ +; + +#ifndef CONFIG_NO_STDOUT_DEBUG +/* Long text divided into parts in order to fit in C89 strings size limits. */ +const char *wpa_supplicant_full_license1 = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n"; +const char *wpa_supplicant_full_license2 = +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n"; +const char *wpa_supplicant_full_license3 = +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n"; +const char *wpa_supplicant_full_license4 = +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"; +const char *wpa_supplicant_full_license5 = +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + +/* Configure default/group WEP keys for static WEP */ +static int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int i, set = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] == 0) + continue; + + set = 1; + wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + (u8 *) "\xff\xff\xff\xff\xff\xff", + i, i == ssid->wep_tx_keyidx, (u8 *) "", 0, + ssid->wep_key[i], ssid->wep_key_len[i]); + } + + return set; +} + + +static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + u8 key[32]; + size_t keylen; + wpa_alg alg; + u8 seq[6] = { 0 }; + + /* IBSS/WPA-None uses only one key (Group) for both receiving and + * sending unicast and multicast packets. */ + + if (ssid->mode != IEEE80211_MODE_IBSS) { + wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) " + "for WPA-None", ssid->mode); + return -1; + } + + if (!ssid->psk_set) { + wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None"); + return -1; + } + + switch (wpa_s->group_cipher) { + case WPA_CIPHER_CCMP: + os_memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + /* WPA-None uses the same Michael MIC key for both TX and RX */ + os_memcpy(key, ssid->psk, 16 + 8); + os_memcpy(key + 16 + 8, ssid->psk + 16, 8); + keylen = 32; + alg = WPA_ALG_TKIP; + break; + default: + wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for " + "WPA-None", wpa_s->group_cipher); + return -1; + } + + /* TODO: should actually remember the previously used seq#, both for TX + * and RX from each STA.. */ + + return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", + 0, 1, seq, 6, key, keylen); +} + + +static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + const u8 *bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", + MAC2STR(bssid)); + wpa_blacklist_add(wpa_s, bssid); + wpa_sm_notify_disassoc(wpa_s->wpa); + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +/** + * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to time out authentication + * @usec: Number of microseconds after which to time out authentication + * + * This function is used to schedule a timeout for the current authentication + * attempt. + */ +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + if (wpa_s->conf && wpa_s->conf->ap_scan == 0 && + wpa_s->driver && IS_WIRED(wpa_s->driver)) + return; + + wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " + "%d usec", sec, usec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); +} + + +/** + * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel authentication timeout scheduled with + * wpa_supplicant_req_auth_timeout() and it is called when authentication has + * been completed. + */ +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_blacklist_del(wpa_s, wpa_s->bssid); +} + + +/** + * wpa_supplicant_initiate_eapol - Configure EAPOL state machine + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to configure EAPOL state machine based on the selected + * authentication mode. + */ +void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) +{ +#ifdef IEEE8021X_EAPOL + struct eapol_config eapol_conf; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); + else + eapol_sm_notify_portControl(wpa_s->eapol, Auto); + + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) { + eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST; + } + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) { + eapol_conf.required_keys |= + EAPOL_REQUIRE_KEY_BROADCAST; + } + + if (wpa_s->conf && wpa_s->driver && IS_WIRED(wpa_s->driver)) { + eapol_conf.required_keys = 0; + } + } + if (wpa_s->conf) + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_conf.eap_disabled = + !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) && + wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && + wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; + eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); +#endif /* IEEE8021X_EAPOL */ +} + + +/** + * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode + * @wpa_s: Pointer to wpa_supplicant data + * @ssid: Configuration data for the network + * + * This function is used to configure WPA state machine and related parameters + * to a mode where WPA is not enabled. This is called as part of the + * authentication configuration when the selected network does not use WPA. + */ +void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int i; + + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + wpa_s->key_mgmt = WPA_KEY_MGMT_WPS; + else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else + wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->mgmt_group_cipher = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] > 5) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; + wpa_s->group_cipher = WPA_CIPHER_WEP104; + break; + } else if (ssid->wep_key_len[i] > 0) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP40; + wpa_s->group_cipher = WPA_CIPHER_WEP40; + break; + } + } + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, + wpa_s->pairwise_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); +#ifdef CONFIG_IEEE80211W + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); +#endif /* CONFIG_IEEE80211W */ + + pmksa_cache_clear_current(wpa_s->wpa); +} + + +static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) +{ + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); + eapol_sm_register_scard_ctx(wpa_s->eapol, NULL); + l2_packet_deinit(wpa_s->l2); + wpa_s->l2 = NULL; + if (wpa_s->l2_br) { + l2_packet_deinit(wpa_s->l2_br); + wpa_s->l2_br = NULL; + } + + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + if (wpa_s->conf != NULL) { + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } + + os_free(wpa_s->confname); + wpa_s->confname = NULL; + + wpa_sm_set_eapol(wpa_s->wpa, NULL); + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + + rsn_preauth_deinit(wpa_s->wpa); + + pmksa_candidate_free(wpa_s->wpa); + wpa_sm_deinit(wpa_s->wpa); + wpa_s->wpa = NULL; + wpa_blacklist_clear(wpa_s); + + wpa_scan_results_free(wpa_s->scan_res); + wpa_s->scan_res = NULL; + + wpa_supplicant_cancel_scan(wpa_s); + wpa_supplicant_cancel_auth_timeout(wpa_s); + + ieee80211_sta_deinit(wpa_s); + + wpas_wps_deinit(wpa_s); +} + + +/** + * wpa_clear_keys - Clear keys configured for the driver + * @wpa_s: Pointer to wpa_supplicant data + * @addr: Previously used BSSID or %NULL if not available + * + * This function clears the encryption keys that has been previously configured + * for the driver. + */ +void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; + + if (wpa_s->keys_cleared) { + /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have + * timing issues with keys being cleared just before new keys + * are set or just after association or something similar. This + * shows up in group key handshake failing often because of the + * client not receiving the first encrypted packets correctly. + * Skipping some of the extra key clearing steps seems to help + * in completing group key handshake more reliably. */ + wpa_printf(MSG_DEBUG, "No keys have been configured - " + "skip key clearing"); + return; + } + + /* MLME-DELETEKEYS.request */ + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); + if (addr) { + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, + 0); + /* MLME-SETPROTECTION.request(None) */ + wpa_drv_mlme_setprotection( + wpa_s, addr, + MLME_SETPROTECTION_PROTECT_TYPE_NONE, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + } + wpa_s->keys_cleared = 1; +} + + +/** + * wpa_supplicant_state_txt - Get the connection state name as a text string + * @state: State (wpa_state; WPA_*) + * Returns: The state name as a printable text string + */ +const char * wpa_supplicant_state_txt(int state) +{ + switch (state) { + case WPA_DISCONNECTED: + return "DISCONNECTED"; + case WPA_INACTIVE: + return "INACTIVE"; + 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"; + } +} + + +/** + * wpa_supplicant_set_state - Set current connection state + * @wpa_s: Pointer to wpa_supplicant data + * @state: The new connection state + * + * This function is called whenever the connection state changes, e.g., + * association is completed for WPA/WPA2 4-Way Handshake is started. + */ +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_states state) +{ + wpa_printf(MSG_DEBUG, "State: %s -> %s", + wpa_supplicant_state_txt(wpa_s->wpa_state), + wpa_supplicant_state_txt(state)); + + wpa_supplicant_dbus_notify_state_change(wpa_s, state, + wpa_s->wpa_state); + + if (state == WPA_COMPLETED && wpa_s->new_connection) { +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) + struct wpa_ssid *ssid = wpa_s->current_ssid; + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " + MACSTR " completed %s [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ? + "(reauth)" : "(auth)", + ssid ? ssid->id : -1, + ssid && ssid->id_str ? ssid->id_str : ""); +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpa_s->new_connection = 0; + wpa_s->reassociated_connection = 1; + wpa_drv_set_operstate(wpa_s, 1); + } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || + state == WPA_ASSOCIATED) { + wpa_s->new_connection = 1; + wpa_drv_set_operstate(wpa_s, 0); + } + wpa_s->wpa_state = state; +} + + +static void wpa_supplicant_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct wpa_supplicant *wpa_s; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d " + "received", sig); + } + eloop_terminate(); +} + + +static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) +{ + wpa_s->pairwise_cipher = 0; + wpa_s->group_cipher = 0; + wpa_s->mgmt_group_cipher = 0; + wpa_s->key_mgmt = 0; + wpa_s->wpa_state = WPA_DISCONNECTED; +} + + +/** + * wpa_supplicant_reload_configuration - Reload configuration data + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success or -1 if configuration parsing failed + * + * This function can be used to request that the configuration data is reloaded + * (e.g., after configuration file change). This function is reloading + * configuration only for one interface, so this may need to be called multiple + * times if %wpa_supplicant is controlling multiple interfaces and all + * interfaces need reconfiguration. + */ +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + struct wpa_config *conf; + int reconf_ctrl; + if (wpa_s->confname == NULL) + return -1; + conf = wpa_config_read(wpa_s->confname); + if (conf == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " + "file '%s' - exiting", wpa_s->confname); + return -1; + } + + reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface + || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && + os_strcmp(conf->ctrl_interface, + wpa_s->conf->ctrl_interface) != 0); + + if (reconf_ctrl && wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + + eapol_sm_invalidate_cached_session(wpa_s->eapol); + wpa_s->current_ssid = NULL; + /* + * TODO: should notify EAPOL SM about changes in opensc_engine_path, + * pkcs11_engine_path, pkcs11_module_path. + */ + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + /* + * Clear forced success to clear EAP state for next + * authentication. + */ + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + } + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_sm_set_config(wpa_s->wpa, NULL); + wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); + rsn_preauth_deinit(wpa_s->wpa); + wpa_config_free(wpa_s->conf); + wpa_s->conf = conf; + if (reconf_ctrl) + wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); + + wpa_supplicant_clear_status(wpa_s); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); + return 0; +} + + +static void wpa_supplicant_reconfig(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct wpa_supplicant *wpa_s; + wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig); + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_supplicant_reload_configuration(wpa_s) < 0) { + eloop_terminate(); + } + } +} + + +static wpa_cipher cipher_suite2driver(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return CIPHER_NONE; + case WPA_CIPHER_WEP40: + return CIPHER_WEP40; + case WPA_CIPHER_WEP104: + return CIPHER_WEP104; + case WPA_CIPHER_CCMP: + return CIPHER_CCMP; + case WPA_CIPHER_TKIP: + default: + return CIPHER_TKIP; + } +} + + +static wpa_key_mgmt key_mgmt2driver(int key_mgmt) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_NONE: + return KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return KEY_MGMT_802_1X_NO_WPA; + case WPA_KEY_MGMT_IEEE8021X: + return KEY_MGMT_802_1X; + case WPA_KEY_MGMT_WPA_NONE: + return KEY_MGMT_WPA_NONE; + case WPA_KEY_MGMT_FT_IEEE8021X: + return KEY_MGMT_FT_802_1X; + case WPA_KEY_MGMT_FT_PSK: + return KEY_MGMT_FT_PSK; + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return KEY_MGMT_802_1X_SHA256; + case WPA_KEY_MGMT_PSK_SHA256: + return KEY_MGMT_PSK_SHA256; + case WPA_KEY_MGMT_WPS: + return KEY_MGMT_WPS; + case WPA_KEY_MGMT_PSK: + default: + return KEY_MGMT_PSK; + } +} + + +static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_ie_data *ie) +{ + int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie); + if (ret) { + if (ret == -2) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE " + "from association info"); + } + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher " + "suites"); + if (!(ie->group_cipher & ssid->group_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " + "cipher 0x%x (mask 0x%x) - reject", + ie->group_cipher, ssid->group_cipher); + return -1; + } + if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise " + "cipher 0x%x (mask 0x%x) - reject", + ie->pairwise_cipher, ssid->pairwise_cipher); + return -1; + } + if (!(ie->key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key " + "management 0x%x (mask 0x%x) - reject", + ie->key_mgmt, ssid->key_mgmt); + return -1; + } + +#ifdef CONFIG_IEEE80211W + if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && + ssid->ieee80211w == IEEE80211W_REQUIRED) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " + "that does not support management frame protection - " + "reject"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_supplicant_set_suites - Set authentication and encryption parameters + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Scan results for the selected BSS, or %NULL if not available + * @ssid: Configuration data for the selected network + * @wpa_ie: Buffer for the WPA/RSN IE + * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the + * used buffer length in case the functions returns success. + * Returns: 0 on success or -1 on failure + * + * This function is used to configure authentication and encryption parameters + * based on the network configuration and scan result for the selected BSS (if + * available). + */ +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, size_t *wpa_ie_len) +{ + struct wpa_ie_data ie; + int sel, proto; + const u8 *bss_wpa, *bss_rsn; + + if (bss) { + bss_wpa = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + bss_rsn = wpa_scan_get_ie(bss, WLAN_EID_RSN); + } else + bss_wpa = bss_rsn = NULL; + + if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && + wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); + proto = WPA_PROTO_RSN; + } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) && + wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); + proto = WPA_PROTO_WPA; + } else if (bss) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); + return -1; + } else { + if (ssid->proto & WPA_PROTO_RSN) + proto = WPA_PROTO_RSN; + else + proto = WPA_PROTO_WPA; + if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) { + os_memset(&ie, 0, sizeof(ie)); + ie.group_cipher = ssid->group_cipher; + ie.pairwise_cipher = ssid->pairwise_cipher; + ie.key_mgmt = ssid->key_mgmt; +#ifdef CONFIG_IEEE80211W + ie.mgmt_group_cipher = + ssid->ieee80211w != NO_IEEE80211W ? + WPA_CIPHER_AES_128_CMAC : 0; +#endif /* CONFIG_IEEE80211W */ + wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based " + "on configuration"); + } else + proto = ie.proto; + } + + wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d " + "pairwise %d key_mgmt %d proto %d", + ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); +#ifdef CONFIG_IEEE80211W + if (ssid->ieee80211w) { + wpa_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d", + ie.mgmt_group_cipher); + } +#endif /* CONFIG_IEEE80211W */ + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, + !!(ssid->proto & WPA_PROTO_RSN)); + + if (bss || !wpa_s->ap_ies_from_associnfo) { + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, + bss_wpa ? 2 + bss_wpa[1] : 0) || + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, + bss_rsn ? 2 + bss_rsn[1] : 0)) + return -1; + } + + sel = ie.group_cipher & ssid->group_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->group_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->group_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); + } else if (sel & WPA_CIPHER_WEP104) { + wpa_s->group_cipher = WPA_CIPHER_WEP104; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); + } else if (sel & WPA_CIPHER_WEP40) { + wpa_s->group_cipher = WPA_CIPHER_WEP40; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher."); + return -1; + } + + sel = ie.pairwise_cipher & ssid->pairwise_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); + } else if (sel & WPA_CIPHER_NONE) { + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " + "cipher."); + return -1; + } + + sel = ie.key_mgmt & ssid->key_mgmt; + if (0) { +#ifdef CONFIG_IEEE80211R + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); + } else if (sel & WPA_KEY_MGMT_FT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + wpa_msg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT 802.1X with SHA256"); + } else if (sel & WPA_KEY_MGMT_PSK_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; + wpa_msg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT PSK with SHA256"); +#endif /* CONFIG_IEEE80211W */ + } else if (sel & WPA_KEY_MGMT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); + } else if (sel & WPA_KEY_MGMT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); + } else if (sel & WPA_KEY_MGMT_WPA_NONE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated " + "key management type."); + return -1; + } + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, + wpa_s->pairwise_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); + +#ifdef CONFIG_IEEE80211W + sel = ie.mgmt_group_cipher; + if (ssid->ieee80211w == NO_IEEE80211W || + !(ie.capabilities & WPA_CAPABILITY_MFPC)) + sel = 0; + if (sel & WPA_CIPHER_AES_128_CMAC) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " + "AES-128-CMAC"); + } else { + wpa_s->mgmt_group_cipher = 0; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); +#endif /* CONFIG_IEEE80211W */ + + if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE."); + return -1; + } + + if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); + else + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); + + return 0; +} + + +/** + * wpa_supplicant_associate - Request association + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Scan results for the selected BSS, or %NULL if not available + * @ssid: Configuration data for the selected network + * + * This function is used to request %wpa_supplicant to associate with a BSS. + */ +void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *bss, struct wpa_ssid *ssid) +{ + u8 wpa_ie[80]; + size_t wpa_ie_len; + int use_crypt, ret, i; + int algs = AUTH_ALG_OPEN_SYSTEM; + wpa_cipher cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + struct wpa_driver_capa capa; + int assoc_failed = 0; + + wpa_s->reassociate = 0; + if (bss) { +#ifdef CONFIG_IEEE80211R + const u8 *md = NULL; +#endif /* CONFIG_IEEE80211R */ + const u8 *ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + ie ? wpa_ssid_txt(ie + 2, ie[1]) : "", bss->freq); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); +#ifdef CONFIG_IEEE80211R + ie = wpa_scan_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) + md = ie + 2; + wpa_sm_set_ft_params(wpa_s->wpa, md, NULL, 0, NULL); + if (md) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(wpa_s->wpa); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && + wpa_s->conf->ap_scan == 2 && + (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Use ap_scan==1 style network selection to find the network + */ + wpa_s->scan_req = 2; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return; +#endif /* CONFIG_WPS */ + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + } + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + + if (wpa_drv_set_mode(wpa_s, ssid->mode)) { + wpa_printf(MSG_WARNING, "Failed to set operating mode"); + assoc_failed = 1; + } + +#ifdef IEEE8021X_EAPOL + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = AUTH_ALG_LEAP; + else + algs |= AUTH_ALG_LEAP; + } + } +#endif /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = 0; + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) + algs |= AUTH_ALG_OPEN_SYSTEM; + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) + algs |= AUTH_ALG_SHARED_KEY; + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) + algs |= AUTH_ALG_LEAP; + wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", + algs); + } + wpa_drv_set_auth_alg(wpa_s, algs); + + if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || + wpa_scan_get_ie(bss, WLAN_EID_RSN)) && + (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256))) { + int try_opportunistic; + try_opportunistic = ssid->proactive_key_caching && + (ssid->proto & WPA_PROTO_RSN); + if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, + wpa_s->current_ssid, + try_opportunistic) == 0) + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + wpa_ie_len = sizeof(wpa_ie); + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites"); + return; + } + } else if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)) { + wpa_ie_len = sizeof(wpa_ie); + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites (no scan " + "results)"); + return; + } +#ifdef CONFIG_WPS + } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + struct wpabuf *wps_ie; + wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); + if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) { + wpa_ie_len = wpabuf_len(wps_ie); + os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len); + } else + wpa_ie_len = 0; + wpabuf_free(wps_ie); + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); +#endif /* CONFIG_WPS */ + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + } + + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); + use_crypt = 1; + cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); + cipher_group = cipher_suite2driver(wpa_s->group_cipher); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) + use_crypt = 0; + if (wpa_set_wep_keys(wpa_s, ssid)) { + use_crypt = 1; + wep_keys_set = 1; + } + } + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) + use_crypt = 0; + +#ifdef IEEE8021X_EAPOL + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 && + !wep_keys_set) { + use_crypt = 0; + } else { + /* Assume that dynamic WEP-104 keys will be used and + * set cipher suites in order for drivers to expect + * encryption. */ + cipher_pairwise = cipher_group = CIPHER_WEP104; + } + } +#endif /* IEEE8021X_EAPOL */ + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key before (and later after) association */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + } + + wpa_drv_set_drop_unencrypted(wpa_s, use_crypt); + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); + os_memset(¶ms, 0, sizeof(params)); + if (bss) { + const u8 *ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + params.bssid = bss->bssid; + params.ssid = ie ? ie + 2 : (u8 *) ""; + params.ssid_len = ie ? ie[1] : 0; + params.freq = bss->freq; + } else { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + } + if (ssid->mode == 1 && ssid->frequency > 0 && params.freq == 0) + params.freq = ssid->frequency; /* Initial channel for IBSS */ + params.wpa_ie = wpa_ie; + params.wpa_ie_len = wpa_ie_len; + params.pairwise_suite = cipher_pairwise; + params.group_suite = cipher_group; + params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.auth_alg = algs; + params.mode = ssid->mode; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + params.wep_key[i] = ssid->wep_key[i]; + params.wep_key_len[i] = ssid->wep_key_len[i]; + } + params.wep_tx_keyidx = ssid->wep_tx_keyidx; + + if (wpa_s->driver_4way_handshake && + (params.key_mgmt_suite == KEY_MGMT_PSK || + params.key_mgmt_suite == KEY_MGMT_FT_PSK)) { + params.passphrase = ssid->passphrase; + if (ssid->psk_set) + params.psk = ssid->psk; + } + +#ifdef CONFIG_IEEE80211W + switch (ssid->ieee80211w) { + case NO_IEEE80211W: + params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION; + break; + case IEEE80211W_OPTIONAL: + params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_OPTIONAL; + break; + case IEEE80211W_REQUIRED: + params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_REQUIRED; + break; + } + if (ssid->ieee80211w != NO_IEEE80211W && bss) { + const u8 *rsn = wpa_scan_get_ie(bss, WLAN_EID_RSN); + struct wpa_ie_data ie; + if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && + ie.capabilities & + (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { + wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " + "require MFP"); + params.mgmt_frame_protection = + MGMT_FRAME_PROTECTION_REQUIRED; + } + } +#endif /* CONFIG_IEEE80211W */ + + if (wpa_s->use_client_mlme) + ret = ieee80211_sta_associate(wpa_s, ¶ms); + else + ret = wpa_drv_associate(wpa_s, ¶ms); + if (ret < 0) { + wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " + "failed"); + /* try to continue anyway; new association will be tried again + * after timeout */ + assoc_failed = 1; + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key after the association just in case association + * cleared the previously configured key. */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + /* No need to timeout authentication since there is no key + * management. */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } else { + /* Timeout for IEEE 802.11 authentication and association */ + int timeout = 60; + + if (assoc_failed) { + /* give IBSS a bit more time */ + timeout = ssid->mode ? 10 : 5; + } else if (wpa_s->conf->ap_scan == 1) { + /* give IBSS a bit more time */ + timeout = ssid->mode ? 20 : 10; + } + wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); + } + + if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + /* Set static WEP keys again */ + wpa_set_wep_keys(wpa_s, ssid); + } + + if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) { + /* + * Do not allow EAP session resumption between different + * network configurations. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + wpa_s->current_ssid = ssid; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(wpa_s); +} + + +/** + * wpa_supplicant_disassociate - Disassociate the current connection + * @wpa_s: Pointer to wpa_supplicant data + * @reason_code: IEEE 802.11 reason code for the disassociate frame + * + * This function is used to request %wpa_supplicant to disassociate with the + * current AP. + */ +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + if (!is_zero_ether_addr(wpa_s->bssid)) { + if (wpa_s->use_client_mlme) + ieee80211_sta_disassociate(wpa_s, reason_code); + else + wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_s->current_ssid = NULL; + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); +} + + +/** + * wpa_supplicant_deauthenticate - Deauthenticate the current connection + * @wpa_s: Pointer to wpa_supplicant data + * @reason_code: IEEE 802.11 reason code for the deauthenticate frame + * + * This function is used to request %wpa_supplicant to deauthenticate from the + * current AP. + */ +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + if (!is_zero_ether_addr(wpa_s->bssid)) { + if (wpa_s->use_client_mlme) + ieee80211_sta_deauthenticate(wpa_s, reason_code); + else + wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, + reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_s->current_ssid = NULL; + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); +} + + +static int wpa_supplicant_get_scan_results_old(struct wpa_supplicant *wpa_s) +{ +#define SCAN_AP_LIMIT 128 + struct wpa_scan_result *results; + int num, i; + struct wpa_scan_results *res; + + results = os_malloc(SCAN_AP_LIMIT * sizeof(struct wpa_scan_result)); + if (results == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate memory for scan " + "results"); + return -1; + } + + num = wpa_drv_get_scan_results(wpa_s, results, SCAN_AP_LIMIT); + wpa_printf(MSG_DEBUG, "Scan results: %d", num); + if (num < 0) { + wpa_printf(MSG_DEBUG, "Failed to get scan results"); + os_free(results); + return -1; + } + if (num > SCAN_AP_LIMIT) { + wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)", + num, SCAN_AP_LIMIT); + num = SCAN_AP_LIMIT; + } + + wpa_scan_results_free(wpa_s->scan_res); + wpa_s->scan_res = NULL; + + /* Convert old scan result data structure to the new one */ + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(results); + return -1; + } + res->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(results); + os_free(res); + return -1; + } + + for (i = 0; i < num; i++) { + struct wpa_scan_result *bss = &results[i]; + struct wpa_scan_res *r; + size_t ie_len; + u8 *pos; + + ie_len = 2 + bss->ssid_len + bss->rsn_ie_len + bss->wpa_ie_len; + if (bss->maxrate) + ie_len += 3; + if (bss->mdie_present) + ie_len += 5; + + r = os_zalloc(sizeof(*r) + ie_len); + if (r == NULL) + break; + + os_memcpy(r->bssid, bss->bssid, ETH_ALEN); + r->freq = bss->freq; + r->caps = bss->caps; + r->qual = bss->qual; + r->noise = bss->noise; + r->level = bss->level; + r->tsf = bss->tsf; + r->ie_len = ie_len; + + pos = (u8 *) (r + 1); + + /* SSID IE */ + *pos++ = WLAN_EID_SSID; + *pos++ = bss->ssid_len; + os_memcpy(pos, bss->ssid, bss->ssid_len); + pos += bss->ssid_len; + + if (bss->maxrate) { + /* Fake Supported Rate IE to include max rate */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = bss->maxrate; + } + + if (bss->rsn_ie_len) { + os_memcpy(pos, bss->rsn_ie, bss->rsn_ie_len); + pos += bss->rsn_ie_len; + } + + if (bss->mdie_present) { + os_memcpy(pos, bss->mdie, 5); + pos += 5; + } + + if (bss->wpa_ie_len) { + os_memcpy(pos, bss->wpa_ie, bss->wpa_ie_len); + pos += bss->wpa_ie_len; + } + + res->res[res->num++] = r; + } + + os_free(results); + wpa_s->scan_res = res; + + return 0; +} + + +/** + * wpa_supplicant_get_scan_results - Get scan results + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is request the current scan results from the driver and stores + * a local copy of the results in wpa_s->scan_res. + */ +int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s) +{ + int ret; + + if (wpa_s->use_client_mlme) { + wpa_scan_results_free(wpa_s->scan_res); + wpa_s->scan_res = ieee80211_sta_get_scan_results(wpa_s); + if (wpa_s->scan_res == NULL) { + wpa_printf(MSG_DEBUG, "Failed to get scan results"); + ret = -1; + } else + ret = 0; + } else if (wpa_s->driver->get_scan_results2 == NULL) + ret = wpa_supplicant_get_scan_results_old(wpa_s); + else { + wpa_scan_results_free(wpa_s->scan_res); + wpa_s->scan_res = wpa_drv_get_scan_results2(wpa_s); + if (wpa_s->scan_res == NULL) { + wpa_printf(MSG_DEBUG, "Failed to get scan results"); + ret = -1; + } else + ret = 0; + } + + if (wpa_s->scan_res) + wpa_scan_sort_results(wpa_s->scan_res); + + return ret; +} + + +/** + * wpa_supplicant_get_ssid - Get a pointer to the current network structure + * @wpa_s: Pointer to wpa_supplicant data + * Returns: A pointer to the current network structure or %NULL on failure + */ +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *entry; + u8 ssid[MAX_SSID_LEN]; + int res; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + int wired; + + if (wpa_s->use_client_mlme) { + if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) { + wpa_printf(MSG_WARNING, "Could not read SSID from " + "MLME."); + return NULL; + } + } else { + res = wpa_drv_get_ssid(wpa_s, ssid); + if (res < 0) { + wpa_printf(MSG_WARNING, "Could not read SSID from " + "driver."); + return NULL; + } + ssid_len = res; + } + + if (wpa_s->use_client_mlme) + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_printf(MSG_WARNING, "Could not read BSSID from driver."); + return NULL; + } + + wired = wpa_s->conf->ap_scan == 0 && wpa_s->driver && + IS_WIRED(wpa_s->driver); + + entry = wpa_s->conf->ssid; + while (entry) { + if (!entry->disabled && + ((ssid_len == entry->ssid_len && + os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#ifdef CONFIG_WPS + if (!entry->disabled && + (entry->key_mgmt & WPA_KEY_MGMT_WPS) && + (entry->ssid == NULL || entry->ssid_len == 0) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#endif /* CONFIG_WPS */ + entry = entry->next; + } + + return NULL; +} + + +static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, + const char *name) +{ + int i; + + if (wpa_s == NULL) + return -1; + + if (wpa_supplicant_drivers[0] == NULL) { + wpa_printf(MSG_ERROR, "No driver interfaces build into " + "wpa_supplicant."); + return -1; + } + + if (name == NULL) { + /* default to first driver in the list */ + wpa_s->driver = wpa_supplicant_drivers[0]; + return 0; + } + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + if (os_strcmp(name, wpa_supplicant_drivers[i]->name) == 0) { + wpa_s->driver = wpa_supplicant_drivers[i]; + return 0; + } + } + + wpa_printf(MSG_ERROR, "Unsupported driver '%s'.\n", name); + return -1; +} + + +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *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 && + (!wpa_s->driver_4way_handshake || + !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->wpa_state != WPA_COMPLETED)) { + /* Timeout for completing IEEE 802.1X and WPA authentication */ + wpa_supplicant_req_auth_timeout( + wpa_s, + (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) ? + 70 : 10, 0); + } + wpa_s->eapol_received++; + + if (wpa_s->countermeasures) { + wpa_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. */ + + os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); + if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) && + eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) + return; + wpa_drv_poll(wpa_s); + if (!wpa_s->driver_4way_handshake) + wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len); + else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + /* + * Set portValid = TRUE here since we are going to skip 4-way + * handshake processing which would normally set portValid. We + * need this to allow the EAPOL state machines to be completed + * without going through EAPOL-Key handshake. + */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + } +} + + +void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, + size_t num_hw_features) +{ + ieee80211_sta_free_hw_features(hw_features, num_hw_features); +} + + +void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct wpa_supplicant *wpa_s = ctx; + ieee80211_sta_rx(wpa_s, buf, len, rx_status); +} + + +/** + * wpa_supplicant_driver_init - Initialize driver interface parameters + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +{ + static int interface_count = 0; + + if (wpa_s->driver->send_eapol) { + const u8 *addr = wpa_drv_get_mac_addr(wpa_s); + if (addr) + os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); + } else { + wpa_s->l2 = l2_packet_init(wpa_s->ifname, + wpa_drv_get_mac_addr(wpa_s), + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s, 0); + if (wpa_s->l2 == NULL) + return -1; + } + + if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { + wpa_printf(MSG_ERROR, "Failed to get own L2 address"); + return -1; + } + + wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + + if (wpa_s->bridge_ifname[0]) { + wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface" + " '%s'", wpa_s->bridge_ifname); + wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname, + wpa_s->own_addr, + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s, + 0); + if (wpa_s->l2_br == NULL) { + wpa_printf(MSG_ERROR, "Failed to open l2_packet " + "connection for the bridge interface '%s'", + wpa_s->bridge_ifname); + return -1; + } + } + + /* Backwards compatibility call to set_wpa() handler. This is called + * only just after init and just before deinit, so these handler can be + * used to implement same functionality. */ + if (wpa_drv_set_wpa(wpa_s, 1) < 0) { + struct wpa_driver_capa capa; + if (wpa_drv_get_capa(wpa_s, &capa) < 0 || + !(capa.flags & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2))) { + wpa_printf(MSG_DEBUG, "Driver does not support WPA."); + /* Continue to allow non-WPA modes to be used. */ + } else { + wpa_printf(MSG_ERROR, "Failed to enable WPA in the " + "driver."); + return -1; + } + } + + wpa_clear_keys(wpa_s, NULL); + + /* Make sure that TKIP countermeasures are not left enabled (could + * happen if wpa_supplicant is killed during countermeasures. */ + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_drv_set_drop_unencrypted(wpa_s, 1); + + wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver"); + wpa_drv_flush_pmkid(wpa_s); + + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + wpa_supplicant_req_scan(wpa_s, interface_count, 100000); + interface_count++; + + return 0; +} + + +static int wpa_supplicant_daemon(const char *pid_file) +{ + wpa_printf(MSG_DEBUG, "Daemonize.."); + return os_daemonize(pid_file); +} + + +static struct wpa_supplicant * wpa_supplicant_alloc(void) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = os_zalloc(sizeof(*wpa_s)); + if (wpa_s == NULL) + return NULL; + wpa_s->scan_req = 1; + + return wpa_s; +} + + +static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, + struct wpa_interface *iface) +{ + wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " + "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, + iface->confname ? iface->confname : "N/A", + iface->driver ? iface->driver : "default", + iface->ctrl_interface ? iface->ctrl_interface : "N/A", + iface->bridge_ifname ? iface->bridge_ifname : "N/A"); + + if (wpa_supplicant_set_driver(wpa_s, iface->driver) < 0) { + return -1; + } + + if (iface->confname) { +#ifdef CONFIG_BACKEND_FILE + wpa_s->confname = os_rel2abs_path(iface->confname); + if (wpa_s->confname == NULL) { + wpa_printf(MSG_ERROR, "Failed to get absolute path " + "for configuration file '%s'.", + iface->confname); + return -1; + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", + iface->confname, wpa_s->confname); +#else /* CONFIG_BACKEND_FILE */ + wpa_s->confname = os_strdup(iface->confname); +#endif /* CONFIG_BACKEND_FILE */ + wpa_s->conf = wpa_config_read(wpa_s->confname); + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "Failed to read or parse " + "configuration '%s'.", wpa_s->confname); + return -1; + } + + /* + * Override ctrl_interface and driver_param if set on command + * line. + */ + if (iface->ctrl_interface) { + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = + os_strdup(iface->ctrl_interface); + } + + if (iface->driver_param) { + os_free(wpa_s->conf->driver_param); + wpa_s->conf->driver_param = + os_strdup(iface->driver_param); + } + } else + wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, + iface->driver_param); + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "\nNo configuration found."); + return -1; + } + + if (iface->ifname == NULL) { + wpa_printf(MSG_ERROR, "\nInterface name is required."); + return -1; + } + if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) { + wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.", + iface->ifname); + return -1; + } + os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname)); + + if (iface->bridge_ifname) { + if (os_strlen(iface->bridge_ifname) >= + sizeof(wpa_s->bridge_ifname)) { + wpa_printf(MSG_ERROR, "\nToo long bridge interface " + "name '%s'.", iface->bridge_ifname); + return -1; + } + os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname, + sizeof(wpa_s->bridge_ifname)); + } + + return 0; +} + + +static int wpa_supplicant_init_iface2(struct wpa_supplicant *wpa_s) +{ + const char *ifname; + struct wpa_driver_capa capa; + + wpa_printf(MSG_DEBUG, "Initializing interface (2) '%s'", + wpa_s->ifname); + + /* RSNA Supplicant Key Management - INITIALIZE */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + + /* Initialize driver interface and register driver event handler before + * L2 receive handler so that association events are processed before + * EAPOL-Key packets if both become available for the same select() + * call. */ + wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); + if (wpa_s->drv_priv == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize driver interface"); + return -1; + } + if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { + wpa_printf(MSG_ERROR, "Driver interface rejected " + "driver_param '%s'", wpa_s->conf->driver_param); + return -1; + } + + ifname = wpa_drv_get_ifname(wpa_s); + if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) { + wpa_printf(MSG_DEBUG, "Driver interface replaced interface " + "name with '%s'", ifname); + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + } + + if (wpa_supplicant_init_wpa(wpa_s) < 0) + return -1; + + wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, + wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname : + NULL); + wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); + + if (wpa_s->conf->dot11RSNAConfigPMKLifetime && + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, + wpa_s->conf->dot11RSNAConfigPMKLifetime)) { + wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigPMKLifetime"); + return -1; + } + + if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold && + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, + wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) { + wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigPMKReauthThreshold"); + return -1; + } + + if (wpa_s->conf->dot11RSNAConfigSATimeout && + wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, + wpa_s->conf->dot11RSNAConfigSATimeout)) { + wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigSATimeout"); + return -1; + } + + if (wpa_supplicant_driver_init(wpa_s) < 0) + return -1; + + if (wpa_s->conf->country[0] && wpa_s->conf->country[1] && + wpa_drv_set_country(wpa_s, wpa_s->conf->country)) { + wpa_printf(MSG_DEBUG, "Failed to set country"); + return -1; + } + + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + + if (wpas_wps_init(wpa_s)) + return -1; + + if (wpa_supplicant_init_eapol(wpa_s) < 0) + return -1; + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); + + wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); + if (wpa_s->ctrl_iface == NULL) { + wpa_printf(MSG_ERROR, + "Failed to initialize control interface '%s'.\n" + "You may have another wpa_supplicant process " + "already running or the file was\n" + "left by an unclean termination of wpa_supplicant " + "in which case you will need\n" + "to manually remove this file before starting " + "wpa_supplicant again.\n", + wpa_s->conf->ctrl_interface); + return -1; + } + + if (wpa_drv_get_capa(wpa_s, &capa) == 0) { + if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { + wpa_s->use_client_mlme = 1; + if (ieee80211_sta_init(wpa_s)) + return -1; + } + if (capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) + wpa_s->driver_4way_handshake = 1; + } + + return 0; +} + + +static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_priv) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + + /* Backwards compatibility call to set_wpa() handler. This is + * called only just after init and just before deinit, so these + * handler can be used to implement same functionality. */ + if (wpa_drv_set_wpa(wpa_s, 0) < 0) { + wpa_printf(MSG_ERROR, "Failed to disable WPA in the " + "driver."); + } + + wpa_drv_set_drop_unencrypted(wpa_s, 0); + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_clear_keys(wpa_s, NULL); + } + + wpas_dbus_unregister_iface(wpa_s); + + wpa_supplicant_cleanup(wpa_s); + + if (wpa_s->drv_priv) + wpa_drv_deinit(wpa_s); +} + + +/** + * wpa_supplicant_add_iface - Add a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @iface: Interface configuration options + * Returns: Pointer to the created interface or %NULL on failure + * + * This function is used to add new network interfaces for %wpa_supplicant. + * This can be called before wpa_supplicant_run() to add interfaces before the + * main event loop has been started. In addition, new interfaces can be added + * dynamically while %wpa_supplicant is already running. This could happen, + * e.g., when a hotplug network adapter is inserted. + */ +struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, + struct wpa_interface *iface) +{ + struct wpa_supplicant *wpa_s; + + if (global == NULL || iface == NULL) + return NULL; + + wpa_s = wpa_supplicant_alloc(); + if (wpa_s == NULL) + return NULL; + + if (wpa_supplicant_init_iface(wpa_s, iface) || + wpa_supplicant_init_iface2(wpa_s)) { + wpa_printf(MSG_DEBUG, "Failed to add interface %s", + iface->ifname); + wpa_supplicant_deinit_iface(wpa_s); + os_free(wpa_s); + return NULL; + } + + wpa_s->global = global; + + /* Register the interface with the dbus control interface */ + if (wpas_dbus_register_iface(wpa_s)) { + wpa_supplicant_deinit_iface(wpa_s); + os_free(wpa_s); + return NULL; + } + + wpa_s->next = global->ifaces; + global->ifaces = wpa_s; + + wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname); + + return wpa_s; +} + + +/** + * wpa_supplicant_remove_iface - Remove a network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @wpa_s: Pointer to the network interface to be removed + * Returns: 0 if interface was removed, -1 if interface was not found + * + * This function can be used to dynamically remove network interfaces from + * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In + * addition, this function is used to remove all remaining interfaces when + * %wpa_supplicant is terminated. + */ +int wpa_supplicant_remove_iface(struct wpa_global *global, + struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *prev; + + /* Remove interface from the global list of interfaces */ + prev = global->ifaces; + if (prev == wpa_s) { + global->ifaces = wpa_s->next; + } else { + while (prev && prev->next != wpa_s) + prev = prev->next; + if (prev == NULL) + return -1; + prev->next = wpa_s->next; + } + + wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname); + + wpa_supplicant_deinit_iface(wpa_s); + os_free(wpa_s); + + return 0; +} + + +/** + * wpa_supplicant_get_iface - Get a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @ifname: Interface name + * Returns: Pointer to the interface or %NULL if not found + */ +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, + const char *ifname) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + return wpa_s; + } + return NULL; +} + + +/** + * wpa_supplicant_init - Initialize %wpa_supplicant + * @params: Parameters for %wpa_supplicant + * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure + * + * This function is used to initialize %wpa_supplicant. After successful + * initialization, the returned data pointer can be used to add and remove + * network interfaces, and eventually, to deinitialize %wpa_supplicant. + */ +struct wpa_global * wpa_supplicant_init(struct wpa_params *params) +{ + struct wpa_global *global; + int ret, i; + + if (params == NULL) + return NULL; + + wpa_debug_open_file(params->wpa_debug_file_path); + + ret = eap_peer_register_methods(); + if (ret) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + if (ret == -2) + wpa_printf(MSG_ERROR, "Two or more EAP methods used " + "the same EAP type."); + return NULL; + } + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + global->params.daemonize = params->daemonize; + global->params.wait_for_monitor = params->wait_for_monitor; + global->params.dbus_ctrl_interface = params->dbus_ctrl_interface; + if (params->pid_file) + global->params.pid_file = os_strdup(params->pid_file); + if (params->ctrl_interface) + global->params.ctrl_interface = + os_strdup(params->ctrl_interface); + wpa_debug_level = global->params.wpa_debug_level = + params->wpa_debug_level; + wpa_debug_show_keys = global->params.wpa_debug_show_keys = + params->wpa_debug_show_keys; + wpa_debug_timestamp = global->params.wpa_debug_timestamp = + params->wpa_debug_timestamp; + + if (eloop_init(global)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + wpa_supplicant_deinit(global); + return NULL; + } + + global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); + if (global->ctrl_iface == NULL) { + wpa_supplicant_deinit(global); + return NULL; + } + + if (global->params.dbus_ctrl_interface) { + global->dbus_ctrl_iface = + wpa_supplicant_dbus_ctrl_iface_init(global); + if (global->dbus_ctrl_iface == NULL) { + wpa_supplicant_deinit(global); + return NULL; + } + } + + for (i = 0; wpa_supplicant_drivers[i]; i++) + global->drv_count++; + if (global->drv_count == 0) { + wpa_printf(MSG_ERROR, "No drivers enabled"); + wpa_supplicant_deinit(global); + return NULL; + } + global->drv_priv = os_zalloc(global->drv_count * sizeof(void *)); + if (global->drv_priv == NULL) { + wpa_supplicant_deinit(global); + return NULL; + } + for (i = 0; wpa_supplicant_drivers[i]; i++) { + if (!wpa_supplicant_drivers[i]->global_init) + continue; + global->drv_priv[i] = wpa_supplicant_drivers[i]->global_init(); + if (global->drv_priv[i] == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize driver " + "'%s'", wpa_supplicant_drivers[i]->name); + wpa_supplicant_deinit(global); + return NULL; + } + } + + return global; +} + + +/** + * wpa_supplicant_run - Run the %wpa_supplicant main event loop + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 after successful event loop run, -1 on failure + * + * This function starts the main event loop and continues running as long as + * there are any remaining events. In most cases, this function is running as + * long as the %wpa_supplicant process in still in use. + */ +int wpa_supplicant_run(struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + + if (global->params.daemonize && + wpa_supplicant_daemon(global->params.pid_file)) + return -1; + + if (global->params.wait_for_monitor) { + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) + if (wpa_s->ctrl_iface) + wpa_supplicant_ctrl_iface_wait( + wpa_s->ctrl_iface); + } + + eloop_register_signal_terminate(wpa_supplicant_terminate, NULL); + eloop_register_signal_reconfig(wpa_supplicant_reconfig, NULL); + + eloop_run(); + + return 0; +} + + +/** + * wpa_supplicant_deinit - Deinitialize %wpa_supplicant + * @global: Pointer to global data from wpa_supplicant_init() + * + * This function is called to deinitialize %wpa_supplicant and to free all + * allocated resources. Remaining network interfaces will also be removed. + */ +void wpa_supplicant_deinit(struct wpa_global *global) +{ + int i; + + if (global == NULL) + return; + + while (global->ifaces) + wpa_supplicant_remove_iface(global, global->ifaces); + + if (global->ctrl_iface) + wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface); + if (global->dbus_ctrl_iface) + wpa_supplicant_dbus_ctrl_iface_deinit(global->dbus_ctrl_iface); + + eap_peer_unregister_methods(); + + for (i = 0; wpa_supplicant_drivers[i] && global->drv_priv; i++) { + if (!global->drv_priv[i]) + continue; + wpa_supplicant_drivers[i]->global_deinit(global->drv_priv[i]); + } + os_free(global->drv_priv); + + eloop_destroy(); + + if (global->params.pid_file) { + os_daemonize_terminate(global->params.pid_file); + os_free(global->params.pid_file); + } + os_free(global->params.ctrl_interface); + + os_free(global); + wpa_debug_close_file(); +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf new file mode 100644 index 0000000000000..43e81a16d2c85 --- /dev/null +++ b/wpa_supplicant/wpa_supplicant.conf @@ -0,0 +1,840 @@ +##### Example wpa_supplicant configuration file ############################### +# +# This file describes configuration file format and lists all available option. +# Please also take a look at simpler configuration examples in 'examples' +# subdirectory. +# +# Empty lines and lines starting with # are ignored + +# NOTE! This file may contain password information and should probably be made +# readable only by root user on multiuser systems. + +# Note: All file paths in this configuration file should use full (absolute, +# not relative to working directory) path in order to allow working directory +# to be changed. This can happen if wpa_supplicant is run in the background. + +# Whether to allow wpa_supplicant to update (overwrite) configuration +# +# This option can be used to allow wpa_supplicant to overwrite configuration +# file whenever configuration is changed (e.g., new network block is added with +# wpa_cli or wpa_gui, or a password is changed). This is required for +# wpa_cli/wpa_gui to be able to store the configuration changes permanently. +# Please note that overwriting configuration file will remove the comments from +# it. +#update_config=1 + +# global configuration (shared by all network blocks) +# +# Parameters for the control interface. If this is specified, wpa_supplicant +# will open a control interface that is available for external programs to +# manage wpa_supplicant. The meaning of this string depends on which control +# interface mechanism is used. For all cases, the existance of this parameter +# in configuration is used to determine whether the control interface is +# enabled. +# +# For UNIX domain sockets (default on Linux and BSD): This is a directory that +# will be created for UNIX domain sockets for listening to requests from +# external programs (CLI/GUI, etc.) for status information and configuration. +# The socket file will be named based on the interface name, so multiple +# wpa_supplicant processes can be run at the same time if more than one +# interface is used. +# /var/run/wpa_supplicant is the recommended directory for sockets and by +# default, wpa_cli will use it when trying to connect with wpa_supplicant. +# +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run wpa_supplicant as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# run as non-root users. However, since the control interface can be used to +# change the network configuration, this access needs to be protected in many +# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you +# want to allow non-root users to use the control interface, add a new group +# and change this value to match with that group. Add users that should have +# control interface access to this group. If this variable is commented out or +# not included in the configuration file, group will not be changed from the +# value it got by default when the directory or socket was created. +# +# When configuring both the directory and group, use following format: +# DIR=/var/run/wpa_supplicant GROUP=wheel +# DIR=/var/run/wpa_supplicant GROUP=0 +# (group can be either group name or gid) +# +# For UDP connections (default on Windows): The value will be ignored. This +# variable is just used to select that the control interface is to be created. +# The value can be set to, e.g., udp (ctrl_interface=udp) +# +# For Windows Named Pipe: This value can be used to set the security descriptor +# for controlling access to the control interface. Security descriptor can be +# set using Security Descriptor String Format (see http://msdn.microsoft.com/ +# library/default.asp?url=/library/en-us/secauthz/security/ +# security_descriptor_string_format.asp). The descriptor string needs to be +# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty +# DACL (which will reject all connections). See README-Windows.txt for more +# information about SDDL string format. +# +ctrl_interface=/var/run/wpa_supplicant + +# IEEE 802.1X/EAPOL version +# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines +# EAPOL version 2. However, there are many APs that do not handle the new +# version number correctly (they seem to drop the frames completely). In order +# to make wpa_supplicant interoperate with these APs, the version number is set +# to 1 by default. This configuration value can be used to set it to the new +# version (2). +eapol_version=1 + +# AP scanning/selection +# By default, wpa_supplicant requests driver to perform AP scanning and then +# uses the scan results to select a suitable AP. Another alternative is to +# allow the driver to take care of AP scanning and selection and use +# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association +# information from the driver. +# 1: wpa_supplicant initiates scanning and AP selection +# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association +# parameters (e.g., WPA IE generation); this mode can also be used with +# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with +# APs (i.e., external program needs to control association). This mode must +# also be used when using wired Ethernet drivers. +# 2: like 0, but associate with APs using security policy and SSID (but not +# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to +# enable operation with hidden SSIDs and optimized roaming; in this mode, +# the network blocks in the configuration file are tried one by one until +# the driver reports successful association; each network block should have +# explicit security policy (i.e., only one option in the lists) for +# key_mgmt, pairwise, group, proto variables +ap_scan=1 + +# EAP fast re-authentication +# By default, fast re-authentication is enabled for all EAP methods that +# support it. This variable can be used to disable fast re-authentication. +# Normally, there is no need to disable this. +fast_reauth=1 + +# OpenSSL Engine support +# These options can be used to load OpenSSL engines. +# The two engines that are supported currently are shown below: +# They are both from the opensc project (http://www.opensc.org/) +# By default no engines are loaded. +# make the opensc engine available +#opensc_engine_path=/usr/lib/opensc/engine_opensc.so +# make the pkcs11 engine available +#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so +# configure the path to the pkcs11 module required by the pkcs11 engine +#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so + +# Dynamic EAP methods +# If EAP methods were built dynamically as shared object files, they need to be +# loaded here before being used in the network blocks. By default, EAP methods +# are included statically in the build, so these lines are not needed +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so + +# Driver interface parameters +# This field can be used to configure arbitrary driver interace parameters. The +# format is specific to the selected driver interface. This field is not used +# in most cases. +#driver_param="field=value" + +# Country code +# The ISO/IEC alpha2 country code for the country in which this device is +# currently operating. +#country=US + +# Maximum lifetime for PMKSA in seconds; default 43200 +#dot11RSNAConfigPMKLifetime=43200 +# Threshold for reauthentication (percentage of PMK lifetime); default 70 +#dot11RSNAConfigPMKReauthThreshold=70 +# Timeout for security association negotiation in seconds; default 60 +#dot11RSNAConfigSATimeout=60 + +# Wi-Fi Protected Setup (WPS) parameters + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless Client + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=cmodel + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: <categ>-<OUI>-<subcateg> +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=1-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +#wps_cred_processing=0 + +# network block +# +# Each network (usually AP's sharing the same SSID) is configured as a separate +# block in this configuration file. The network blocks are in preference order +# (the first match is used). +# +# network block fields: +# +# disabled: +# 0 = this network can be used (default) +# 1 = this network block is disabled (can be enabled through ctrl_iface, +# e.g., with wpa_cli or wpa_gui) +# +# id_str: Network identifier string for external scripts. This value is passed +# to external action script through wpa_cli as WPA_ID_STR environment +# variable to make it easier to do network specific configuration. +# +# ssid: SSID (mandatory); either as an ASCII string with double quotation or +# as hex string; network name +# +# scan_ssid: +# 0 = do not scan this SSID with specific Probe Request frames (default) +# 1 = scan with SSID-specific Probe Request frames (this can be used to +# find APs that do not accept broadcast SSID or use multiple SSIDs; +# this will add latency to scanning, so enable this only when needed) +# +# bssid: BSSID (optional); if set, this network block is used only when +# associating with the AP using the configured BSSID +# +# priority: priority group (integer) +# By default, all networks will get same priority group (0). If some of the +# networks are more desirable, this field can be used to change the order in +# which wpa_supplicant goes through the networks when selecting a BSS. The +# priority groups will be iterated in decreasing priority (i.e., the larger the +# priority value, the sooner the network is matched against the scan results). +# Within each priority group, networks will be selected based on security +# policy, signal strength, etc. +# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not +# using this priority to select the order for scanning. Instead, they try the +# networks in the order that used in the configuration file. +# +# mode: IEEE 802.11 operation mode +# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) +# 1 = IBSS (ad-hoc, peer-to-peer) +# 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. +# +# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g., +# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial +# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode. +# In addition, this value is only used by the station that creates the IBSS. If +# an IBSS network with the configured SSID is already present, the frequency of +# the network will be used instead of this configured value. +# +# proto: list of accepted protocols +# WPA = WPA/IEEE 802.11i/D3.0 +# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) +# If not set, this defaults to: WPA RSN +# +# key_mgmt: list of accepted authenticated key management protocols +# WPA-PSK = WPA pre-shared key (this requires 'psk' field) +# WPA-EAP = WPA using EAP authentication +# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically +# generated WEP keys +# NONE = WPA is not used; plaintext or static WEP could be used +# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms +# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# If not set, this defaults to: WPA-PSK WPA-EAP +# +# auth_alg: list of allowed IEEE 802.11 authentication algorithms +# OPEN = Open System authentication (required for WPA/WPA2) +# SHARED = Shared Key authentication (requires static WEP keys) +# LEAP = LEAP/Network EAP (only used with LEAP) +# If not set, automatic selection is used (Open System with LEAP enabled if +# LEAP is allowed as one of the EAP methods). +# +# pairwise: list of accepted pairwise (unicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# NONE = Use only Group Keys (deprecated, should not be included if APs support +# pairwise keys) +# If not set, this defaults to: CCMP TKIP +# +# group: list of accepted group (broadcast/multicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key +# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] +# If not set, this defaults to: CCMP TKIP WEP104 WEP40 +# +# psk: WPA preshared key; 256-bit pre-shared key +# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., +# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be +# generated using the passphrase and SSID). ASCII passphrase must be between +# 8 and 63 characters (inclusive). +# This field is not needed, if WPA-EAP is used. +# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys +# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant +# startup and reconfiguration time can be optimized by generating the PSK only +# only when the passphrase or SSID has actually changed. +# +# eapol_flags: IEEE 802.1X/EAPOL options (bit field) +# Dynamic WEP key required for non-WPA mode +# bit0 (1): require dynamically generated unicast WEP key +# bit1 (2): require dynamically generated broadcast WEP key +# (3 = require both keys; default) +# Note: When using wired authentication, eapol_flags must be set to 0 for the +# authentication to be completed successfully. +# +# mixed_cell: This option can be used to configure whether so called mixed +# cells, i.e., networks that use both plaintext and encryption in the same +# SSID, are allowed when selecting a BSS form scan results. +# 0 = disabled (default) +# 1 = enabled +# +# proactive_key_caching: +# Enable/disable opportunistic PMKSA caching for WPA2. +# 0 = disabled (default) +# 1 = enabled +# +# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or +# hex without quotation, e.g., 0102030405) +# wep_tx_keyidx: Default WEP key index (TX) (0..3) +# +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is +# allowed. This is only used with RSN/WPA2. +# 0 = disabled (default) +# 1 = enabled +#peerkey=1 +# +# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to +# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. +# +# Following fields are only used with internal EAP implementation. +# eap: space-separated list of accepted EAP methods +# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# cannot be used with WPA; to be used as a Phase 2 method +# with EAP-PEAP or EAP-TTLS) +# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# OTP = EAP-OTP (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# GTC = EAP-GTC (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# TLS = EAP-TLS (client and server certificate) +# PEAP = EAP-PEAP (with tunnelled EAP authentication) +# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 +# authentication) +# If not set, all compiled in methods are allowed. +# +# identity: Identity string for EAP +# This field is also used to configure user NAI for +# EAP-PSK/PAX/SAKE/GPSK. +# anonymous_identity: Anonymous identity string for EAP (to be used as the +# unencrypted identity with EAP types that support different tunnelled +# identity, e.g., EAP-TTLS) +# password: Password string for EAP. This field can include either the +# plaintext password (using ASCII or hex string) or a NtPasswordHash +# (16-byte MD4 hash of password) in hash:<32 hex digits> format. +# NtPasswordHash can only be used when the password is for MSCHAPv2 or +# MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). +# EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit +# PSK) is also configured using this field. For EAP-GPSK, this is a +# variable length PSK. +# ca_cert: File path to CA certificate file (PEM/DER). This file can have one +# or more trusted CA certificates. If ca_cert and ca_path are not +# included, server certificate will not be verified. This is insecure and +# a trusted CA certificate should always be configured when using +# EAP-TLS/TTLS/PEAP. Full path should be used since working directory may +# change when wpa_supplicant is run in the background. +# On Windows, trusted CA certificates can be loaded from the system +# certificate store by setting this to cert_store://<name>, e.g., +# ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# ca_path: Directory path for CA certificate files (PEM). This path may +# contain multiple CA certificates in OpenSSL format. Common use for this +# is to point to system trusted CA list which is often installed into +# directory like /etc/ssl/certs. If configured, these certificates are +# added to the list of trusted CAs. ca_cert may also be included in that +# case, but it is not required. +# client_cert: File path to client certificate file (PEM/DER) +# Full path should be used since working directory may change when +# wpa_supplicant is run in the background. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read from +# the PKCS#12 file in this case. Full path should be used since working +# directory may change when wpa_supplicant is run in the background. +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# cert://substring_to_match +# hash://certificate_thumbprint_in_hex +# for example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key_passwd: Password for private key file (if left out, this will be +# asked through control interface) +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA +# authentication does not use this configuration. However, it is possible +# setup RSA to use ephemeral DH key exchange. In addition, ciphers with +# DSA keys always use ephemeral DH keys. This can be used to achieve +# forward secrecy. If the file is in DSA parameters format, it will be +# automatically converted into DH params. +# subject_match: Substring to be matched against the subject of the +# authentication server certificate. If this string is set, the server +# sertificate is only accepted if it contains this string in the subject. +# The subject string is in following format: +# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com +# altsubject_match: Semicolon separated string of entries to be matched against +# the alternative subject name of the authentication server certificate. +# If this string is set, the server sertificate is only accepted if it +# contains one of the entries in an alternative subject name extension. +# altSubjectName string is in following format: TYPE:VALUE +# Example: EMAIL:server@example.com +# Example: DNS:server.example.com;DNS:server2.example.com +# Following types are supported: EMAIL, DNS, URI +# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters +# (string with field-value pairs, e.g., "peapver=0" or +# "peapver=1 peaplabel=1") +# 'peapver' can be used to force which PEAP version (0 or 1) is used. +# 'peaplabel=1' can be used to force new label, "client PEAP encryption", +# to be used during key derivation when PEAPv1 or newer. Most existing +# PEAPv1 implementation seem to be using the old label, "client EAP +# encryption", and wpa_supplicant is now using that as the default value. +# Some servers, e.g., Radiator, may require peaplabel=1 configuration to +# interoperate with PEAPv1; see eap_testing.txt for more details. +# 'peap_outer_success=0' can be used to terminate PEAP authentication on +# tunneled EAP-Success. This is required with some RADIUS servers that +# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., +# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode) +# include_tls_length=1 can be used to force wpa_supplicant to include +# TLS Message Length field in all TLS messages even if they are not +# fragmented. +# sim_min_num_chal=3 can be used to configure EAP-SIM to require three +# challenges (by default, it accepts 2 or 3) +# result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use +# protected result indication. +# 'crypto_binding' option can be used to control PEAPv0 cryptobinding +# behavior: +# * 0 = do not use cryptobinding (default) +# * 1 = use cryptobinding if server supports it +# * 2 = require cryptobinding +# EAP-WSC (WPS) uses following options: pin=<Device Password> or +# pbc=1. +# phase2: Phase2 (inner authentication with TLS tunnel) parameters +# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# Following certificate/private key fields are used in inner Phase2 +# authentication when using EAP-TTLS or EAP-PEAP. +# ca_cert2: File path to CA certificate file. This file can have one or more +# trusted CA certificates. If ca_cert2 and ca_path2 are not included, +# server certificate will not be verified. This is insecure and a trusted +# CA certificate should always be configured. +# ca_path2: Directory path for CA certificate files (PEM) +# client_cert2: File path to client certificate file +# private_key2: File path to client private key file +# private_key2_passwd: Password for private key file +# dh_file2: File path to DH/DSA parameters file (in PEM format) +# subject_match2: Substring to be matched against the subject of the +# authentication server certificate. +# altsubject_match2: Substring to be matched against the alternative subject +# name of the authentication server certificate. +# +# fragment_size: Maximum EAP fragment size in bytes (default 1398). +# This value limits the fragment size for EAP methods that support +# fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set +# small enough to make the EAP messages fit in MTU of the network +# interface used for EAPOL. The default value is suitable for most +# cases. +# +# EAP-FAST variables: +# pac_file: File path for the PAC entries. wpa_supplicant will need to be able +# to create this file and write updates to it when PAC is being +# provisioned or refreshed. Full path to the file should be used since +# working directory may change when wpa_supplicant is run in the +# background. Alternatively, a named configuration blob can be used by +# setting this to blob://<blob name> +# phase1: fast_provisioning option can be used to enable in-line provisioning +# of EAP-FAST credentials (PAC): +# 0 = disabled, +# 1 = allow unauthenticated provisioning, +# 2 = allow authenticated provisioning, +# 3 = allow both unauthenticated and authenticated provisioning +# fast_max_pac_list_len=<num> option can be used to set the maximum +# number of PAC entries to store in a PAC list (default: 10) +# fast_pac_format=binary option can be used to select binary format for +# storing PAC entries in order to save some space (the default +# text format uses about 2.5 times the size of minimal binary +# format) +# +# wpa_supplicant supports number of "EAP workarounds" to work around +# interoperability issues with incorrectly behaving authentication servers. +# These are enabled by default because some of the issues are present in large +# number of authentication servers. Strict EAP conformance mode can be +# configured by disabling workarounds with eap_workaround=0. + +# Example blocks: + +# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers +network={ + ssid="simple" + psk="very secret passphrase" + priority=5 +} + +# Same as previous, but request SSID-specific scanning (for APs that reject +# broadcast SSID) +network={ + ssid="second ssid" + scan_ssid=1 + psk="very secret passphrase" + priority=2 +} + +# Only WPA-PSK is used. Any valid cipher combination is accepted. +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb + priority=2 +} + +# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=TKIP + group=TKIP + psk="not so secure passphrase" + wpa_ptk_rekey=600 +} + +# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104 +# or WEP40 as the group cipher will not be accepted. +network={ + ssid="example" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + priority=1 +} + +# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel +# (e.g., Radiator) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=1" + phase2="auth=MSCHAPV2" + priority=10 +} + +# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the +# unencrypted use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + priority=2 +} + +# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted +# use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MSCHAPV2" +} + +# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner +# authentication. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + # Phase1 / outer authentication + anonymous_identity="anonymous@example.com" + ca_cert="/etc/cert/ca.pem" + # Phase 2 / inner authentication + phase2="autheap=TLS" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" + priority=2 +} + +# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and +# group cipher. +network={ + ssid="example" + bssid=00:11:22:33:44:55 + proto=WPA RSN + key_mgmt=WPA-PSK WPA-EAP + pairwise=CCMP + group=CCMP + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb +} + +# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP +# and all valid ciphers. +network={ + ssid=00010203 + psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +} + + +# EAP-SIM with a GSM SIM or USIM +network={ + ssid="eap-sim-test" + key_mgmt=WPA-EAP + eap=SIM + pin="1234" + pcsc="" +} + + +# EAP-PSK +network={ + ssid="eap-psk-test" + key_mgmt=WPA-EAP + eap=PSK + anonymous_identity="eap_psk_user" + password=06b4be19da289f475aa46a33cb793029 + identity="eap_psk_user@example.com" +} + + +# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using +# EAP-TLS for authentication and key generation; require both unicast and +# broadcast WEP keys. +network={ + ssid="1x-test" + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +# LEAP with dynamic WEP keys +network={ + ssid="leap-example" + key_mgmt=IEEE8021X + eap=LEAP + identity="user" + password="foobar" +} + +# EAP-IKEv2 using shared secrets for both server and peer authentication +network={ + ssid="ikev2-example" + key_mgmt=WPA-EAP + eap=IKEV2 + identity="user" + password="foobar" +} + +# EAP-FAST with WPA (WPA or WPA2) +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="/etc/wpa_supplicant.eap-fast-pac" +} + +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="blob://eap-fast-pac" +} + +# Plaintext connection (no WPA, no IEEE 802.1X) +network={ + ssid="plaintext-test" + key_mgmt=NONE +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) +network={ + ssid="static-wep-test" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key +# IEEE 802.11 authentication +network={ + ssid="static-wep-test2" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 + auth_alg=SHARED +} + + +# IBSS/ad-hoc network with WPA-None/TKIP. +network={ + ssid="test adhoc" + mode=1 + frequency=2412 + proto=WPA + key_mgmt=WPA-NONE + pairwise=NONE + group=TKIP + psk="secret passphrase" +} + + +# Catch all example that allows more or less all configuration modes +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" +} + +# Example of EAP-TLS with smartcard (openssl engine) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TLS + proto=RSN + pairwise=CCMP TKIP + group=CCMP TKIP + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + + engine=1 + + # The engine configured here must be available. Look at + # OpenSSL engine support in the global section. + # The key available through the engine must be the private key + # matching the client certificate configured above. + + # use the opensc engine + #engine_id="opensc" + #key_id="45" + + # use the pkcs11 engine + engine_id="pkcs11" + key_id="id_45" + + # Optional PIN configuration; this can be left out and PIN will be + # asked through the control interface + pin="1234" +} + +# Example configuration showing how to use an inlined blob as a CA certificate +# data instead of using external file +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="blob://exampleblob" + priority=20 +} + +blob-base64-exampleblob={ +SGVsbG8gV29ybGQhCg== +} + + +# Wildcard match for SSID (plaintext APs only). This example select any +# open AP regardless of its SSID. +network={ + key_mgmt=NONE +} diff --git a/wpa_supplicant/wpa_supplicant.nsi b/wpa_supplicant/wpa_supplicant.nsi new file mode 100644 index 0000000000000..2783ca3c8e649 --- /dev/null +++ b/wpa_supplicant/wpa_supplicant.nsi @@ -0,0 +1,108 @@ +!define PRODUCT_NAME "wpa_supplicant" +!define PRODUCT_VERSION "@WPAVER@" +!define PRODUCT_PUBLISHER "Jouni Malinen" + +Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" +outfile "../wpa_supplicant-@WPAVER@.exe" + +installDir "$PROGRAMFILES\wpa_supplicant" + +Page Directory +Page InstFiles + +section -Prerequisites + SetOutPath $INSTDIR\Prerequisites + MessageBox MB_YESNO "Install WinPcap?" /SD IDYES IDNO endWinPcap + File "/opt/Qt-Win/files/WinPcap_4_0_2.exe" + ExecWait "$INSTDIR\Prerequisites\WinPcap_4_0_2.exe" + Goto endWinPcap + endWinPcap: +sectionEnd + + +section + setOutPath $INSTDIR + + File wpa_gui.exe + File wpa_cli.exe + File COPYING + File README + File README-Windows.txt + File win_example.reg + File win_if_list.exe + File wpa_passphrase.exe + File wpa_supplicant.conf + File wpa_supplicant.exe + File wpasvc.exe + + File /opt/Qt-Win/files/mingwm10.dll + File /opt/Qt-Win/files/QtCore4.dll + File /opt/Qt-Win/files/QtGui4.dll + + WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_level" 0 + WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_show_keys" 0 + WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_timestamp" 0 + WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_use_file" 0 + + WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "ap_scan" 2 + WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "update_config" 1 + WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" 1 + DeleteRegValue HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" + + WriteRegDWORD HKLM "Software\wpa_supplicant\interfaces" "dummy" 1 + DeleteRegValue HKLM "Software\wpa_supplicant\interfaces" "dummy" + + writeUninstaller "$INSTDIR\uninstall.exe" + + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ + "DisplayName" "wpa_supplicant" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ + "UninstallString" "$INSTDIR\uninstall.exe" + + CreateDirectory "$SMPROGRAMS\wpa_supplicant" + CreateShortCut "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" "$INSTDIR\wpa_gui.exe" + CreateShortCut "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" "$INSTDIR\uninstall.exe" + + ExecWait "$INSTDIR\wpasvc.exe reg" +sectionEnd + + +Function un.onInit + MessageBox MB_YESNO "This will uninstall wpa_supplicant. Continue?" IDYES NoAbort + Abort + NoAbort: +FunctionEnd + +section "uninstall" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" + delete "$INSTDIR\uninstall.exe" + + ExecWait "$INSTDIR\wpasvc.exe unreg" + + DeleteRegKey HKLM "Software\wpa_supplicant" + + delete "$INSTDIR\wpa_gui.exe" + delete "$INSTDIR\wpa_cli.exe" + delete "$INSTDIR\COPYING" + delete "$INSTDIR\README" + delete "$INSTDIR\README-Windows.txt" + delete "$INSTDIR\win_example.reg" + delete "$INSTDIR\win_if_list.exe" + delete "$INSTDIR\wpa_passphrase.exe" + delete "$INSTDIR\wpa_supplicant.conf" + delete "$INSTDIR\wpa_supplicant.exe" + delete "$INSTDIR\wpasvc.exe" + + delete "$INSTDIR\mingwm10.dll" + delete "$INSTDIR\QtCore4.dll" + delete "$INSTDIR\QtGui4.dll" + + delete "$INSTDIR\Prerequisites\WinPcap_4_0_2.exe" + rmdir "$INSTDIR\Prerequisites" + + rmdir "$INSTDIR" + + delete "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" + delete "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" + rmdir "$SMPROGRAMS\wpa_supplicant" +sectionEnd diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h new file mode 100644 index 0000000000000..5e4dcc1b9fcad --- /dev/null +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -0,0 +1,763 @@ +/* + * wpa_supplicant - Internal definitions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_SUPPLICANT_I_H +#define WPA_SUPPLICANT_I_H + +#include "drivers/driver.h" + +extern const char *wpa_supplicant_version; +extern const char *wpa_supplicant_license; +#ifndef CONFIG_NO_STDOUT_DEBUG +extern const char *wpa_supplicant_full_license1; +extern const char *wpa_supplicant_full_license2; +extern const char *wpa_supplicant_full_license3; +extern const char *wpa_supplicant_full_license4; +extern const char *wpa_supplicant_full_license5; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; + + +struct wpa_scan_result; +struct wpa_sm; +struct wpa_supplicant; + +/* + * Forward declarations of private structures used within the ctrl_iface + * backends. Other parts of wpa_supplicant do not have access to data stored in + * these structures. + */ +struct ctrl_iface_priv; +struct ctrl_iface_global_priv; +struct ctrl_iface_dbus_priv; + +/** + * struct wpa_interface - Parameters for wpa_supplicant_add_iface() + */ +struct wpa_interface { + /** + * confname - Configuration name (file or profile) name + * + * This can also be %NULL when a configuration file is not used. In + * that case, ctrl_interface must be set to allow the interface to be + * configured. + */ + const char *confname; + + /** + * ctrl_interface - Control interface parameter + * + * If a configuration file is not used, this variable can be used to + * set the ctrl_interface parameter that would have otherwise been read + * from the configuration file. If both confname and ctrl_interface are + * set, ctrl_interface is used to override the value from configuration + * file. + */ + const char *ctrl_interface; + + /** + * driver - Driver interface name, or %NULL to use the default driver + */ + const char *driver; + + /** + * driver_param - Driver interface parameters + * + * If a configuration file is not used, this variable can be used to + * set the driver_param parameters that would have otherwise been read + * from the configuration file. If both confname and driver_param are + * set, driver_param is used to override the value from configuration + * file. + */ + const char *driver_param; + + /** + * ifname - Interface name + */ + const char *ifname; + + /** + * bridge_ifname - Optional bridge interface name + * + * If the driver interface (ifname) is included in a Linux bridge + * device, the bridge interface may need to be used for receiving EAPOL + * frames. This can be enabled by setting this variable to enable + * receiving of EAPOL frames from an additional interface. + */ + const char *bridge_ifname; +}; + +/** + * struct wpa_params - Parameters for wpa_supplicant_init() + */ +struct wpa_params { + /** + * daemonize - Run %wpa_supplicant in the background + */ + int daemonize; + + /** + * wait_for_monitor - Wait for a monitor program before starting + */ + int wait_for_monitor; + + /** + * pid_file - Path to a PID (process ID) file + * + * If this and daemonize are set, process ID of the background process + * will be written to the specified file. + */ + char *pid_file; + + /** + * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO) + */ + int wpa_debug_level; + + /** + * wpa_debug_show_keys - Whether keying material is included in debug + * + * This parameter can be used to allow keying material to be included + * in debug messages. This is a security risk and this option should + * not be enabled in normal configuration. If needed during + * development or while troubleshooting, this option can provide more + * details for figuring out what is happening. + */ + int wpa_debug_show_keys; + + /** + * wpa_debug_timestamp - Whether to include timestamp in debug messages + */ + int wpa_debug_timestamp; + + /** + * ctrl_interface - Global ctrl_iface path/parameter + */ + char *ctrl_interface; + + /** + * dbus_ctrl_interface - Enable the DBus control interface + */ + int dbus_ctrl_interface; + + /** + * wpa_debug_file_path - Path of debug file or %NULL to use stdout + */ + const char *wpa_debug_file_path; +}; + +/** + * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces + * + * This structure is initialized by calling wpa_supplicant_init() when starting + * %wpa_supplicant. + */ +struct wpa_global { + struct wpa_supplicant *ifaces; + struct wpa_params params; + struct ctrl_iface_global_priv *ctrl_iface; + struct ctrl_iface_dbus_priv *dbus_ctrl_iface; + void **drv_priv; + size_t drv_count; +}; + + +struct wpa_client_mlme { +#ifdef CONFIG_CLIENT_MLME + enum { + IEEE80211_DISABLED, IEEE80211_AUTHENTICATE, + IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED, + IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED + } state; + u8 prev_bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u16 aid; + u16 ap_capab, capab; + u8 *extra_ie; /* to be added to the end of AssocReq */ + size_t extra_ie_len; + u8 *extra_probe_ie; /* to be added to the end of ProbeReq */ + size_t extra_probe_ie_len; + wpa_key_mgmt key_mgmt; + + /* The last AssocReq/Resp IEs */ + u8 *assocreq_ies, *assocresp_ies; + size_t assocreq_ies_len, assocresp_ies_len; + + int auth_tries, assoc_tries; + + unsigned int ssid_set:1; + unsigned int bssid_set:1; + unsigned int prev_bssid_set:1; + unsigned int authenticated:1; + unsigned int associated:1; + unsigned int probereq_poll:1; + unsigned int use_protection:1; + unsigned int create_ibss:1; + unsigned int mixed_cell:1; + unsigned int wmm_enabled:1; + + struct os_time last_probe; + +#define IEEE80211_AUTH_ALG_OPEN BIT(0) +#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) +#define IEEE80211_AUTH_ALG_LEAP BIT(2) + unsigned int auth_algs; /* bitfield of allowed auth algs */ + int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ + int auth_transaction; + + struct os_time ibss_join_req; + u8 *probe_resp; /* ProbeResp template for IBSS */ + size_t probe_resp_len; + u32 supp_rates_bits; + + int wmm_last_param_set; + + int sta_scanning; + int scan_hw_mode_idx; + int scan_channel_idx; + enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; + struct os_time last_scan_completed; + int scan_oper_channel; + int scan_oper_freq; + int scan_oper_phymode; + u8 scan_ssid[32]; + size_t scan_ssid_len; + int scan_skip_11b; + + struct ieee80211_sta_bss *sta_bss_list; +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; + + int cts_protect_erp_frames; + + int phymode; /* current mode; WPA_MODE_IEEE80211A, .. */ + struct wpa_hw_modes *modes; + size_t num_modes; + unsigned int hw_modes; /* bitfield of allowed hardware modes; + * (1 << MODE_*) */ + int num_curr_rates; + struct wpa_rate_data *curr_rates; + int freq; /* The current frequency in MHz */ + int channel; /* The current IEEE 802.11 channel number */ + +#ifdef CONFIG_IEEE80211R + u8 current_md[6]; + u8 *ft_ies; + size_t ft_ies_len; +#endif /* CONFIG_IEEE80211R */ + +#else /* CONFIG_CLIENT_MLME */ + int dummy; /* to keep MSVC happy */ +#endif /* CONFIG_CLIENT_MLME */ +}; + +/** + * struct wpa_supplicant - Internal data for wpa_supplicant interface + * + * This structure contains the internal data for core wpa_supplicant code. This + * should be only used directly from the core code. However, a pointer to this + * data is used from other files as an arbitrary context pointer in calls to + * core functions. + */ +struct wpa_supplicant { + struct wpa_global *global; + struct wpa_supplicant *next; + struct l2_packet_data *l2; + struct l2_packet_data *l2_br; + unsigned char own_addr[ETH_ALEN]; + char ifname[100]; +#ifdef CONFIG_CTRL_IFACE_DBUS + char *dbus_path; +#endif /* CONFIG_CTRL_IFACE_DBUS */ + char bridge_ifname[16]; + + char *confname; + struct wpa_config *conf; + int countermeasures; + os_time_t last_michael_mic_error; + u8 bssid[ETH_ALEN]; + u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this + * field contains the targer BSSID. */ + int reassociate; /* reassociation requested */ + int disconnected; /* all connections disabled; i.e., do no reassociate + * before this has been cleared */ + struct wpa_ssid *current_ssid; + int ap_ies_from_associnfo; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int mgmt_group_cipher; + + void *drv_priv; /* private data used by driver_ops */ + + struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; + * NULL = not yet initialized (start + * with broadcast SSID) + * BROADCAST_SSID_SCAN = broadcast + * SSID was used in the previous scan + */ +#define BROADCAST_SSID_SCAN ((struct wpa_ssid *) 1) + + struct wpa_scan_results *scan_res; + + struct wpa_driver_ops *driver; + int interface_removed; /* whether the network interface has been + * removed */ + struct wpa_sm *wpa; + struct eapol_sm *eapol; + + struct ctrl_iface_priv *ctrl_iface; + + wpa_states wpa_state; + int new_connection; + int reassociated_connection; + + int eapol_received; /* number of EAPOL packets received after the + * previous association event */ + + struct scard_data *scard; + + unsigned char last_eapol_src[ETH_ALEN]; + + int keys_cleared; + + struct wpa_blacklist *blacklist; + + int scan_req; /* manual scan request; this forces a scan even if there + * are no enabled networks in the configuration */ + int scan_res_tried; /* whether ap_scan=1 mode has tried to fetch scan + * results without a new scan request; this is used + * to speed up the first association if the driver + * has already available scan results. */ + int scan_runs; /* number of scan runs since WPS was started */ + + struct wpa_client_mlme mlme; + int use_client_mlme; + int driver_4way_handshake; + + int pending_mic_error_report; + int pending_mic_error_pairwise; + int mic_errors_seen; /* Michael MIC errors with the current PTK */ + + struct wps_context *wps; + int wps_success; /* WPS success event received */ + int blacklist_cleared; +}; + + +/* wpa_supplicant.c */ +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 wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *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_res *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_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_show_license(void); + +struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, + struct wpa_interface *iface); +int wpa_supplicant_remove_iface(struct wpa_global *global, + struct wpa_supplicant *wpa_s); +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, + const char *ifname); +struct wpa_global * wpa_supplicant_init(struct wpa_params *params); +int wpa_supplicant_run(struct wpa_global *global); +void wpa_supplicant_deinit(struct wpa_global *global); + +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +/* scan.c */ +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); + +/* events.c */ +void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); + +/* driver_ops */ +static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s, + const char *ifname) +{ + if (wpa_s->driver->init2) + return wpa_s->driver->init2(wpa_s, ifname, wpa_s->global); + if (wpa_s->driver->init) { + return wpa_s->driver->init(wpa_s, ifname); + } + return NULL; +} + +static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->deinit) + wpa_s->driver->deinit(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s, + const char *param) +{ + if (wpa_s->driver->set_param) + return wpa_s->driver->set_param(wpa_s->drv_priv, param); + return 0; +} + +static inline int wpa_drv_set_drop_unencrypted(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_drop_unencrypted) { + return wpa_s->driver->set_drop_unencrypted(wpa_s->drv_priv, + enabled); + } + return -1; +} + +static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_countermeasures) { + return wpa_s->driver->set_countermeasures(wpa_s->drv_priv, + enabled); + } + return -1; +} + +static inline int wpa_drv_set_auth_alg(struct wpa_supplicant *wpa_s, + int auth_alg) +{ + if (wpa_s->driver->set_auth_alg) { + return wpa_s->driver->set_auth_alg(wpa_s->drv_priv, + auth_alg); + } + return -1; +} + +static inline int wpa_drv_set_wpa(struct wpa_supplicant *wpa_s, int enabled) +{ + if (wpa_s->driver->set_wpa) { + return wpa_s->driver->set_wpa(wpa_s->drv_priv, enabled); + } + return 0; +} + +static inline int wpa_drv_set_mode(struct wpa_supplicant *wpa_s, int mode) +{ + if (wpa_s->driver->set_mode) { + return wpa_s->driver->set_mode(wpa_s->drv_priv, mode); + } + return 0; +} + +static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + if (wpa_s->driver->associate) { + return wpa_s->driver->associate(wpa_s->drv_priv, params); + } + return -1; +} + +static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len) +{ + if (wpa_s->driver->scan) { + return wpa_s->driver->scan(wpa_s->drv_priv, ssid, ssid_len); + } + return -1; +} + +static inline int wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *results, + size_t max_size) +{ + if (wpa_s->driver->get_scan_results) { + return wpa_s->driver->get_scan_results(wpa_s->drv_priv, + results, max_size); + } + return -1; +} + +static inline struct wpa_scan_results * wpa_drv_get_scan_results2( + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_scan_results2) + return wpa_s->driver->get_scan_results2(wpa_s->drv_priv); + return NULL; +} + +static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid) +{ + if (wpa_s->driver->get_bssid) { + return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid); + } + return -1; +} + +static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid) +{ + if (wpa_s->driver->get_ssid) { + return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid); + } + return -1; +} + +static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (wpa_s->driver->set_key) { + wpa_s->keys_cleared = 0; + return wpa_s->driver->set_key(wpa_s->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, + key, key_len); + } + return -1; +} + +static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->deauthenticate) { + return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->disassociate) { + return wpa_s->driver->disassociate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->add_pmkid) { + return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid); + } + return -1; +} + +static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->remove_pmkid) { + return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid, + pmkid); + } + return -1; +} + +static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->flush_pmkid) { + return wpa_s->driver->flush_pmkid(wpa_s->drv_priv); + } + return -1; +} + +static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s, + struct wpa_driver_capa *capa) +{ + if (wpa_s->driver->get_capa) { + return wpa_s->driver->get_capa(wpa_s->drv_priv, capa); + } + return -1; +} + +static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->poll) { + wpa_s->driver->poll(wpa_s->drv_priv); + } +} + +static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_ifname) { + return wpa_s->driver->get_ifname(wpa_s->drv_priv); + } + return NULL; +} + +static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_mac_addr) { + return wpa_s->driver->get_mac_addr(wpa_s->drv_priv); + } + return NULL; +} + +static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s, + const u8 *dst, u16 proto, + const u8 *data, size_t data_len) +{ + if (wpa_s->driver->send_eapol) + return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto, + data, data_len); + return -1; +} + +static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s, + int state) +{ + if (wpa_s->driver->set_operstate) + return wpa_s->driver->set_operstate(wpa_s->drv_priv, state); + return 0; +} + +static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s, + const u8 *addr, int protect_type, + int key_type) +{ + if (wpa_s->driver->mlme_setprotection) + return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr, + protect_type, + key_type); + return 0; +} + +static inline struct wpa_hw_modes * +wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes, + u16 *flags) +{ + if (wpa_s->driver->get_hw_feature_data) + return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv, + num_modes, flags); + return NULL; +} + +static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s, + wpa_hw_mode phymode, int chan, + int freq) +{ + if (wpa_s->driver->set_channel) + return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode, + chan, freq); + return -1; +} + +static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len) +{ + if (wpa_s->driver->set_ssid) { + return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid, + ssid_len); + } + return -1; +} + +static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + if (wpa_s->driver->set_bssid) { + return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid); + } + return -1; +} + +static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, + const char *alpha2) +{ + if (wpa_s->driver->set_country) + return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2); + return 0; +} + +static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len) +{ + if (wpa_s->driver->send_mlme) + return wpa_s->driver->send_mlme(wpa_s->drv_priv, + data, data_len); + return -1; +} + +static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *supp_rates, + size_t supp_rates_len) +{ + if (wpa_s->driver->mlme_add_sta) + return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr, + supp_rates, supp_rates_len); + return -1; +} + +static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (wpa_s->driver->mlme_remove_sta) + return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr); + return -1; +} + +static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s, + const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (wpa_s->driver->update_ft_ies) + return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md, + ies, ies_len); + return -1; +} + +static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s, + u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + if (wpa_s->driver->send_ft_action) + return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action, + target_ap, ies, ies_len); + return -1; +} + +static inline int wpa_drv_set_probe_req_ie(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + if (wpa_s->driver->set_probe_req_ie) + return wpa_s->driver->set_probe_req_ie(wpa_s->drv_priv, ies, + ies_len); + return -1; +} + +#endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c new file mode 100644 index 0000000000000..2b96aa7071ea8 --- /dev/null +++ b/wpa_supplicant/wpas_glue.c @@ -0,0 +1,642 @@ +/* + * WPA Supplicant - Glue code to setup EAPOL and RSN modules + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "eloop.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "wpa_common.h" +#include "wpa_supplicant_i.h" +#include "pmksa_cache.h" +#include "mlme.h" +#include "ieee802_11_defs.h" +#include "wpa_ctrl.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" + + +#ifndef CONFIG_NO_CONFIG_BLOBS +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) +static void wpa_supplicant_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); + if (wpa_s->conf->update_config) { + int ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to update config after " + "blob set"); + } + } +} + + +static const struct wpa_config_blob * +wpa_supplicant_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} +#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */ +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +/** + * wpa_ether_send - Send Ethernet frame + * @wpa_s: Pointer to wpa_supplicant data + * @dest: Destination MAC address + * @proto: Ethertype in host byte order + * @buf: Frame payload starting from IEEE 802.1X header + * @len: Frame payload length + * Returns: >=0 on success, <0 on failure + */ +static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + if (wpa_s->l2) { + return l2_packet_send(wpa_s->l2, dest, proto, buf, len); + } + + return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); +} +#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */ + + +#ifdef IEEE8021X_EAPOL + +/** + * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator + * @ctx: Pointer to wpa_supplicant data (wpa_s) + * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*) + * @buf: EAPOL payload (after IEEE 802.1X header) + * @len: EAPOL payload length + * Returns: >=0 on success, <0 on failure + * + * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame + * to the current Authenticator. + */ +static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg, *dst, bssid[ETH_ALEN]; + size_t msglen; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { + /* Current SSID is not using IEEE 802.1X/EAP, so drop possible + * EAPOL frames (mainly, EAPOL-Start) from EAPOL state + * machines. */ + wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X " + "mode (type=%d len=%lu)", type, + (unsigned long) len); + return -1; + } + + if (pmksa_cache_get_current(wpa_s->wpa) && + type == IEEE802_1X_TYPE_EAPOL_START) { + /* Trying to use PMKSA caching - do not send EAPOL-Start frames + * since they will trigger full EAPOL authentication. */ + wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " + "EAPOL-Start"); + return -1; + } + + if (is_zero_ether_addr(wpa_s->bssid)) { + wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an " + "EAPOL frame"); + if (wpa_drv_get_bssid(wpa_s, bssid) == 0 && + !is_zero_ether_addr(bssid)) { + dst = bssid; + wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR + " from the driver as the EAPOL destination", + MAC2STR(dst)); + } else { + dst = wpa_s->last_eapol_src; + wpa_printf(MSG_DEBUG, "Using the source address of the" + " last received EAPOL frame " MACSTR " as " + "the EAPOL destination", + MAC2STR(dst)); + } + } else { + /* BSSID was already set (from (Re)Assoc event, so use it as + * the EAPOL destination. */ + dst = wpa_s->bssid; + } + + msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL); + if (msg == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst)); + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen); + res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen); + os_free(msg); + return res; +} + + +/** + * wpa_eapol_set_wep_key - set WEP key for the driver + * @ctx: Pointer to wpa_supplicant data (wpa_s) + * @unicast: 1 = individual unicast key, 0 = broadcast key + * @keyidx: WEP key index (0..3) + * @key: Pointer to key data + * @keylen: Key length in bytes + * Returns: 0 on success or < 0 on error. + */ +static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx, + const u8 *key, size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 : + WPA_CIPHER_WEP104; + if (unicast) + wpa_s->pairwise_cipher = cipher; + else + wpa_s->group_cipher = cipher; + } + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + unicast ? wpa_s->bssid : + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, unicast, (u8 *) "", 0, key, keylen); +} + + +static void wpa_supplicant_aborted_cached(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_sm_aborted_cached(wpa_s->wpa); +} + + +static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, + void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + int res, pmk_len; + u8 pmk[PMK_LEN]; + + wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully", + success ? "" : "un"); + + if (wpas_wps_eapol_cb(wpa_s) > 0) + return; + + if (!success) { + /* + * Make sure we do not get stuck here waiting for long EAPOL + * timeout if the AP does not disconnect in case of + * authentication failure. + */ + wpa_supplicant_req_auth_timeout(wpa_s, 2, 0); + } + + if (!success || !wpa_s->driver_4way_handshake) + return; + + if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) + return; + + wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way " + "handshake"); + + pmk_len = PMK_LEN; + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } + + if (res) { + wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state " + "machines"); + return; + } + + if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk, + pmk_len)) { + wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver"); + } + + wpa_supplicant_cancel_scan(wpa_s); + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + +} + + +static void wpa_supplicant_notify_eapol_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete"); + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE); + } else { + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } +} + +#endif /* IEEE8021X_EAPOL */ + + +#ifndef CONFIG_NO_WPA + +static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + size_t i; + int ret = 0; + struct wpa_scan_res *curr = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *ie; + + if (wpa_s->scan_res == NULL) + return -1; + + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *r = wpa_s->scan_res->res[i]; + if (os_memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0) + continue; + ie = wpa_scan_get_ie(r, WLAN_EID_SSID); + if (ssid == NULL || + ((ie && ie[1] == ssid->ssid_len && + os_memcmp(ie + 2, ssid->ssid, ssid->ssid_len) == 0) || + ssid->ssid_len == 0)) { + curr = r; + break; + } + } + + if (curr) { + ie = wpa_scan_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE); + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) + ret = -1; + + ie = wpa_scan_get_ie(curr, WLAN_EID_RSN); + if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) + ret = -1; + } else { + ret = -1; + } + + return ret; +} + + +static int wpa_supplicant_get_beacon_ie(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_get_beacon_ie(wpa_s) == 0) { + return 0; + } + + /* No WPA/RSN IE found in the cached scan results. Try to get updated + * scan results from the driver. */ + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + return -1; + } + + return wpa_get_beacon_ie(wpa_s); +} + + +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos); +} + + +static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto, + const u8 *buf, size_t len) +{ + return wpa_ether_send(wpa_s, dest, proto, buf, len); +} + + +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s) +{ + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static void _wpa_supplicant_set_state(void *wpa_s, wpa_states state) +{ + wpa_supplicant_set_state(wpa_s, state); +} + + +/** + * wpa_supplicant_get_state - Get the connection state + * @wpa_s: Pointer to wpa_supplicant data + * Returns: The current connection state (WPA_*) + */ +static wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s) +{ + return wpa_s->wpa_state; +} + + +static wpa_states _wpa_supplicant_get_state(void *wpa_s) +{ + return wpa_supplicant_get_state(wpa_s); +} + + +static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) +{ + wpa_supplicant_disassociate(wpa_s, reason_code); + /* Schedule a scan to make sure we continue looking for networks */ + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +{ + wpa_supplicant_deauthenticate(wpa_s, reason_code); + /* Schedule a scan to make sure we continue looking for networks */ + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void * wpa_supplicant_get_network_ctx(void *wpa_s) +{ + return wpa_supplicant_get_ssid(wpa_s); +} + + +static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->use_client_mlme) { + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + return 0; + } + return wpa_drv_get_bssid(wpa_s, bssid); +} + + +static int wpa_supplicant_set_key(void *_wpa_s, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_supplicant *wpa_s = _wpa_s; + if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) { + /* Clear the MIC error counter when setting a new PTK. */ + wpa_s->mic_errors_seen = 0; + } + return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, + key, key_len); +} + + +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, + int protection_type, + int key_type) +{ + return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type, + key_type); +} + + +static int wpa_supplicant_add_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + return wpa_drv_add_pmkid(wpa_s, bssid, pmkid); +} + + +static int wpa_supplicant_remove_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid); +} + + +#ifdef CONFIG_IEEE80211R +static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md, + const u8 *ies, size_t ies_len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->use_client_mlme) + return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len); + return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len); +} + + +static int wpa_supplicant_send_ft_action(void *ctx, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->use_client_mlme) + return ieee80211_sta_send_ft_action(wpa_s, action, target_ap, + ies, ies_len); + return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len); +} +#endif /* CONFIG_IEEE80211R */ + +#endif /* CONFIG_NO_WPA */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void wpa_supplicant_eap_param_needed(void *ctx, const char *field, + const char *txt) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid = wpa_s->current_ssid; + char *buf; + size_t buflen; + int len; + + if (ssid == NULL) + return; + + buflen = 100 + os_strlen(txt) + ssid->ssid_len; + buf = os_malloc(buflen); + if (buf == NULL) + return; + len = os_snprintf(buf, buflen, + WPA_CTRL_REQ "%s-%d:%s needed for SSID ", + field, ssid->id, txt); + if (len < 0 || (size_t) len >= buflen) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, "%s", buf); + os_free(buf); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define wpa_supplicant_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) +{ +#ifdef IEEE8021X_EAPOL + struct eapol_ctx *ctx; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context."); + return -1; + } + + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->eapol_send_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; + ctx->eapol_send = wpa_supplicant_eapol_send; + ctx->set_wep_key = wpa_eapol_set_wep_key; + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; + ctx->aborted_cached = wpa_supplicant_aborted_cached; +#ifdef EAP_TLS_OPENSSL + 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; +#endif /* EAP_TLS_OPENSSL */ + ctx->wps = wpa_s->wps; + ctx->eap_param_needed = wpa_supplicant_eap_param_needed; + ctx->cb = wpa_supplicant_eapol_cb; + ctx->cb_ctx = wpa_s; + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + os_free(ctx); + wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state " + "machines."); + return -1; + } +#endif /* IEEE8021X_EAPOL */ + + return 0; +} + + +int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) +{ +#ifndef CONFIG_NO_WPA + struct wpa_sm_ctx *ctx; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate WPA context."); + return -1; + } + + ctx->ctx = wpa_s; + ctx->set_state = _wpa_supplicant_set_state; + ctx->get_state = _wpa_supplicant_get_state; + ctx->deauthenticate = _wpa_supplicant_deauthenticate; + ctx->disassociate = _wpa_supplicant_disassociate; + ctx->set_key = wpa_supplicant_set_key; + ctx->get_network_ctx = wpa_supplicant_get_network_ctx; + ctx->get_bssid = wpa_supplicant_get_bssid; + ctx->ether_send = _wpa_ether_send; + ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie; + ctx->alloc_eapol = _wpa_alloc_eapol; + ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout; + ctx->add_pmkid = wpa_supplicant_add_pmkid; + ctx->remove_pmkid = wpa_supplicant_remove_pmkid; +#ifndef CONFIG_NO_CONFIG_BLOBS + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ + ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection; +#ifdef CONFIG_IEEE80211R + ctx->update_ft_ies = wpa_supplicant_update_ft_ies; + ctx->send_ft_action = wpa_supplicant_send_ft_action; +#endif /* CONFIG_IEEE80211R */ + + wpa_s->wpa = wpa_sm_init(ctx); + if (wpa_s->wpa == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPA state " + "machine"); + return -1; + } +#endif /* CONFIG_NO_WPA */ + + return 0; +} + + +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct rsn_supp_config conf; + if (ssid) { + os_memset(&conf, 0, sizeof(conf)); + conf.network_ctx = ssid; + conf.peerkey_enabled = ssid->peerkey; + conf.allowed_pairwise_cipher = ssid->pairwise_cipher; +#ifdef IEEE8021X_EAPOL + conf.eap_workaround = ssid->eap_workaround; + conf.eap_conf_ctx = &ssid->eap; +#endif /* IEEE8021X_EAPOL */ + conf.ssid = ssid->ssid; + conf.ssid_len = ssid->ssid_len; + conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey; + } + wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); +} diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h new file mode 100644 index 0000000000000..b571e4df1714b --- /dev/null +++ b/wpa_supplicant/wpas_glue.h @@ -0,0 +1,23 @@ +/* + * WPA Supplicant - Glue code to setup EAPOL and RSN modules + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPAS_GLUE_H +#define WPAS_GLUE_H + +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s); +int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s); +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +#endif /* WPAS_GLUE_H */ diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c new file mode 100644 index 0000000000000..9b73601762597 --- /dev/null +++ b/wpa_supplicant/wps_supplicant.c @@ -0,0 +1,770 @@ +/* + * wpa_supplicant / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" +#include "config.h" +#include "eap_peer/eap.h" +#include "wpa_supplicant_i.h" +#include "eloop.h" +#include "uuid.h" +#include "wpa_ctrl.h" +#include "ctrl_iface_dbus.h" +#include "eap_common/eap_wsc_common.h" +#include "blacklist.h" +#include "wps_supplicant.h" + +#define WPS_PIN_SCAN_IGNORE_SEL_REG 3 + +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_clear_wps(struct wpa_supplicant *wpa_s); + + +int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wps_success && + wpa_s->current_ssid && + eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { + const u8 *bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + + wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR + " did not succeed - continue trying to find " + "suitable AP", MAC2STR(bssid)); + wpa_blacklist_add(wpa_s, bssid); + + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, + wpa_s->blacklist_cleared ? 5 : 0, 0); + wpa_s->blacklist_cleared = 0; + return 1; + } + + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && + !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " + "try to associate with the received credential"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return 1; + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting " + "for external credential processing"); + wpas_clear_wps(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return 1; + } + + return 0; +} + + +static int wpa_supplicant_wps_cred(void *ctx, + const struct wps_credential *cred) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if ((wpa_s->conf->wps_cred_processing == 1 || + wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) { + size_t blen = cred->cred_attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, + cred->cred_attr, cred->cred_attr_len); + wpa_msg(wpa_s, MSG_INFO, "%s%s", + WPS_EVENT_CRED_RECEIVED, buf); + os_free(buf); + } + wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); + } else + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED); + + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", + cred->cred_attr, cred->cred_attr_len); + + if (wpa_s->conf->wps_cred_processing == 1) + return 0; + + if (cred->auth_type != WPS_AUTH_OPEN && + cred->auth_type != WPS_AUTH_SHARED && + cred->auth_type != WPS_AUTH_WPAPSK && + cred->auth_type != WPS_AUTH_WPA2PSK) { + wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for " + "unsupported authentication type %d", + cred->auth_type); + return 0; + } + + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " + "on the received credential"); + os_free(ssid->eap.identity); + ssid->eap.identity = NULL; + ssid->eap.identity_len = 0; + os_free(ssid->eap.phase1); + ssid->eap.phase1 = NULL; + os_free(ssid->eap.eap_methods); + ssid->eap.eap_methods = NULL; + } else { + wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " + "received credential"); + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + } + + wpa_config_set_network_defaults(ssid); + + os_free(ssid->ssid); + ssid->ssid = os_malloc(cred->ssid_len); + if (ssid->ssid) { + os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len); + ssid->ssid_len = cred->ssid_len; + } + + switch (cred->encr_type) { + case WPS_ENCR_NONE: + break; + case WPS_ENCR_WEP: + if (cred->key_len > 0 && cred->key_len <= MAX_WEP_KEY_LEN && + cred->key_idx < NUM_WEP_KEYS) { + os_memcpy(ssid->wep_key[cred->key_idx], cred->key, + cred->key_len); + ssid->wep_key_len[cred->key_idx] = cred->key_len; + ssid->wep_tx_keyidx = cred->key_idx; + } + break; + case WPS_ENCR_TKIP: + ssid->pairwise_cipher = WPA_CIPHER_TKIP; + break; + case WPS_ENCR_AES: + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + break; + } + + switch (cred->auth_type) { + case WPS_AUTH_OPEN: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_NONE; + ssid->proto = 0; + break; + case WPS_AUTH_SHARED: + ssid->auth_alg = WPA_AUTH_ALG_SHARED; + ssid->key_mgmt = WPA_KEY_MGMT_NONE; + ssid->proto = 0; + break; + case WPS_AUTH_WPAPSK: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->proto = WPA_PROTO_WPA; + break; + case WPS_AUTH_WPA: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + ssid->proto = WPA_PROTO_WPA; + break; + case WPS_AUTH_WPA2: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + ssid->proto = WPA_PROTO_RSN; + break; + case WPS_AUTH_WPA2PSK: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->proto = WPA_PROTO_RSN; + break; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) { + if (cred->key_len == 2 * PMK_LEN) { + if (hexstr2bin((const char *) cred->key, ssid->psk, + PMK_LEN)) { + wpa_printf(MSG_ERROR, "WPS: Invalid Network " + "Key"); + return -1; + } + ssid->psk_set = 1; + } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) { + os_free(ssid->passphrase); + ssid->passphrase = os_malloc(cred->key_len + 1); + if (ssid->passphrase == NULL) + return -1; + os_memcpy(ssid->passphrase, cred->key, cred->key_len); + ssid->passphrase[cred->key_len] = '\0'; + wpa_config_update_psk(ssid); + } else { + wpa_printf(MSG_ERROR, "WPS: Invalid Network Key " + "length %lu", + (unsigned long) cred->key_len); + return -1; + } + } + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration"); + return -1; + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + + return 0; +} + + +static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d) +{ + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D + "dev_password_id=%d config_error=%d", + m2d->dev_password_id, m2d->config_error); +} + + +static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg); + wpas_clear_wps(wpa_s); +} + + +static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); + wpa_s->wps_success = 1; +} + + +static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct wpa_supplicant *wpa_s = ctx; + switch (event) { + case WPS_EV_M2D: + wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d); + break; + case WPS_EV_FAIL: + wpa_supplicant_wps_event_fail(wpa_s, &data->fail); + break; + case WPS_EV_SUCCESS: + wpa_supplicant_wps_event_success(wpa_s); + break; + case WPS_EV_PWD_AUTH_FAIL: + break; + } +} + + +enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) +{ + if (eap_is_wps_pbc_enrollee(&ssid->eap) || + eap_is_wps_pin_enrollee(&ssid->eap)) + return WPS_REQ_ENROLLEE; + else + return WPS_REQ_REGISTRAR; +} + + +static void wpas_clear_wps(struct wpa_supplicant *wpa_s) +{ + int id; + struct wpa_ssid *ssid; + + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + /* Remove any existing WPS network from configuration */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + if (ssid == wpa_s->current_ssid) + wpa_s->current_ssid = NULL; + id = ssid->id; + } else + id = -1; + ssid = ssid->next; + if (id >= 0) + wpa_config_remove_network(wpa_s->conf, id); + } +} + + +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " + "out"); + wpas_clear_wps(wpa_s); +} + + +static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, + int registrar, const u8 *bssid) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return NULL; + wpa_config_set_network_defaults(ssid); + if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 || + wpa_config_set(ssid, "eap", "WSC", 0) < 0 || + wpa_config_set(ssid, "identity", registrar ? + "\"" WSC_ID_REGISTRAR "\"" : + "\"" WSC_ID_ENROLLEE "\"", 0) < 0) { + wpa_config_remove_network(wpa_s->conf, ssid->id); + return NULL; + } + + if (bssid) { + size_t i; + struct wpa_scan_res *res; + + os_memcpy(ssid->bssid, bssid, ETH_ALEN); + ssid->bssid_set = 1; + + /* Try to get SSID from scan results */ + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) + return ssid; /* Could not find any scan results */ + + for (i = 0; i < wpa_s->scan_res->num; i++) { + const u8 *ie; + + res = wpa_s->scan_res->res[i]; + if (os_memcmp(bssid, res->bssid, ETH_ALEN) != 0) + continue; + + ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ie == NULL) + break; + os_free(ssid->ssid); + ssid->ssid = os_malloc(ie[1]); + if (ssid->ssid == NULL) + break; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + break; + } + } + + return ssid; +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) +{ + struct wpa_ssid *ssid; + + /* Mark all other networks disabled and trigger reassociation */ + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = ssid != selected; + ssid = ssid->next; + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_s->scan_runs = 0; + wpa_s->wps_success = 0; + wpa_s->blacklist_cleared = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_ssid *ssid; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return 0; +} + + +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin) +{ + struct wpa_ssid *ssid; + char val[30]; + unsigned int rpin = 0; + + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + if (pin) + os_snprintf(val, sizeof(val), "\"pin=%s\"", pin); + else { + rpin = wps_generate_pin(); + os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin); + } + wpa_config_set(ssid, "phase1", val, 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return rpin; +} + + +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin) +{ + struct wpa_ssid *ssid; + char val[30]; + + if (!pin) + return -1; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 1, bssid); + if (ssid == NULL) + return -1; + os_snprintf(val, sizeof(val), "\"pin=%s\"", pin); + wpa_config_set(ssid, "phase1", val, 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return 0; +} + + +static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len) +{ + wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for " + "STA " MACSTR, MAC2STR(mac_addr)); + wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); + + /* TODO */ + + return 0; +} + + +static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + char uuid[40], txt[400]; + int len; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid); + len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR + " [%s|%s|%s|%s|%s|%d-%08X-%d]", + uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, + dev->model_number, dev->serial_number, + dev->categ, dev->oui, dev->sub_categ); + if (len > 0 && len < (int) sizeof(txt)) + wpa_printf(MSG_INFO, "%s", txt); +} + + +int wpas_wps_init(struct wpa_supplicant *wpa_s) +{ + struct wps_context *wps; + struct wps_registrar_config rcfg; + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = wpa_supplicant_wps_cred; + wps->event_cb = wpa_supplicant_wps_event; + wps->cb_ctx = wpa_s; + + wps->dev.device_name = wpa_s->conf->device_name; + wps->dev.manufacturer = wpa_s->conf->manufacturer; + wps->dev.model_name = wpa_s->conf->model_name; + wps->dev.model_number = wpa_s->conf->model_number; + wps->dev.serial_number = wpa_s->conf->serial_number; + if (wpa_s->conf->device_type) { + char *pos; + u8 oui[4]; + /* <categ>-<OUI>-<subcateg> */ + wps->dev.categ = atoi(wpa_s->conf->device_type); + pos = os_strchr(wpa_s->conf->device_type, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + if (hexstr2bin(pos, oui, 4)) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI"); + os_free(wps); + return -1; + } + wps->dev.oui = WPA_GET_BE32(oui); + pos = os_strchr(pos, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + wps->dev.sub_categ = atoi(pos); + } + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); + wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */ + os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN); + if (is_nil_uuid(wpa_s->conf->uuid)) { + uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", + wps->uuid, WPS_UUID_LEN); + } else + os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); + + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + + os_memset(&rcfg, 0, sizeof(rcfg)); + rcfg.new_psk_cb = wpas_wps_new_psk_cb; + rcfg.pin_needed_cb = wpas_wps_pin_needed_cb; + rcfg.cb_ctx = wpa_s; + + wps->registrar = wps_registrar_init(wps, &rcfg); + if (wps->registrar == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar"); + os_free(wps); + return -1; + } + + wpa_s->wps = wps; + + return 0; +} + + +void wpas_wps_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + if (wpa_s->wps == NULL) + return; + + wps_registrar_deinit(wpa_s->wps->registrar); + os_free(wpa_s->wps->network_key); + os_free(wpa_s->wps); + wpa_s->wps = NULL; +} + + +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + return -1; + + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + if (!wps_is_selected_pbc_registrar(wps_ie)) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PBC Registrar"); + wpabuf_free(wps_ie); + return 0; + } + + /* TODO: overlap detection */ + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Active PBC)"); + wpabuf_free(wps_ie); + return 1; + } + + if (eap_is_wps_pin_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + /* + * Start with WPS APs that advertise active PIN Registrar and + * allow any WPS AP after third scan since some APs do not set + * Selected Registrar attribute properly when using external + * Registrar. + */ + if (!wps_is_selected_pin_registrar(wps_ie)) { + if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PIN Registrar"); + wpabuf_free(wps_ie); + return 0; + } + wpa_printf(MSG_DEBUG, " selected based on WPS IE"); + } else { + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Active PIN)"); + } + wpabuf_free(wps_ie); + return 1; + } + + if (wps_ie) { + wpa_printf(MSG_DEBUG, " selected based on WPS IE"); + wpabuf_free(wps_ie); + return 1; + } + + return -1; +} + + +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie = NULL; + int ret = 0; + + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { + /* allow wildcard SSID for WPS PBC */ + ret = 1; + } + } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && + (wps_is_selected_pin_registrar(wps_ie) || + wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { + /* allow wildcard SSID for WPS PIN */ + ret = 1; + } + } + + if (!ret && ssid->bssid_set && + os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) { + /* allow wildcard SSID due to hardcoded BSSID match */ + ret = 1; + } + + wpabuf_free(wps_ie); + + return ret; +} + + +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid) +{ + const u8 *sel_uuid, *uuid; + size_t i; + struct wpabuf *wps_ie; + int ret = 0; + + if (!eap_is_wps_pbc_enrollee(&ssid->eap)) + return 0; + + /* Make sure that only one AP is in active PBC mode */ + wps_ie = wpa_scan_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE); + if (wps_ie) + sel_uuid = wps_get_uuid_e(wps_ie); + else + sel_uuid = NULL; + + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *bss = wpa_s->scan_res->res[i]; + struct wpabuf *ie; + if (bss == selected) + continue; + ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!ie) + continue; + if (!wps_is_selected_pbc_registrar(ie)) { + wpabuf_free(ie); + continue; + } + uuid = wps_get_uuid_e(ie); + if (sel_uuid == NULL || uuid == NULL || + os_memcmp(sel_uuid, uuid, 16) != 0) { + ret = 1; /* PBC overlap */ + wpabuf_free(ie); + break; + } + + /* TODO: verify that this is reasonable dual-band situation */ + + wpabuf_free(ie); + } + + wpabuf_free(wps_ie); + + return ret; +} + + +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) +{ + size_t i; + + if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED) + return; + + for (i = 0; i < wpa_s->scan_res->num; i++) { + struct wpa_scan_res *bss = wpa_s->scan_res->res[i]; + struct wpabuf *ie; + ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!ie) + continue; + if (wps_is_selected_pbc_registrar(ie)) + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC); + else if (wps_is_selected_pin_registrar(ie)) + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN); + else + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE); + wpabuf_free(ie); + break; + } +} + + +int wpas_wps_searching(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled) + return 1; + } + + return 0; +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h new file mode 100644 index 0000000000000..8f81dc4bda10d --- /dev/null +++ b/wpa_supplicant/wps_supplicant.h @@ -0,0 +1,95 @@ +/* + * wpa_supplicant / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_SUPPLICANT_H +#define WPS_SUPPLICANT_H + +#ifdef CONFIG_WPS + +#include "wps/wps.h" +#include "wps/wps_defs.h" + +int wpas_wps_init(struct wpa_supplicant *wpa_s); +void wpas_wps_deinit(struct wpa_supplicant *wpa_s); +int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); +enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid); +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin); +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin); +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid); +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s); +int wpas_wps_searching(struct wpa_supplicant *wpa_s); + +#else /* CONFIG_WPS */ + +static inline int wpas_wps_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) +{ + return 0; +} + +static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return -1; +} + +static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return 0; +} + +static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *selected, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +#endif /* CONFIG_WPS */ + +#endif /* WPS_SUPPLICANT_H */ |